Skip to main content

max / makenotwork

Declare what each host is, and check it before building or shipping there Bento and Sando knew a host's name, ssh target, architecture and capabilities and not what the box was, so everything downstream was rediscovered by probing or by a build failing: an ldd guard on the node before the symlink swap, a glibc_check in a recipe, a rebuilt astra regressing silently. ops-core gains `base_image`: a BaseImage (`id/version` from /etc/os-release, parsed not stringly, on Platform's reasoning), a probe, and a check. Bento's Host and Sando's Node gain `base_image` and `libc`. Bento checks in the release preflight barrier beside the commit check; Sando checks before a single byte is pushed, ahead of the rsync and well ahead of the ldd guard. Silence is a skip here, not a refusal, which is where this deliberately departs from Placement::check. macOS and Windows have no /etc/os-release, and base equality is not the compatibility requirement anyway: fw13 is pop/24.04 and prod is ubuntu/24.04, two bases sharing glibc 2.39, and demanding equal bases would refuse every MNW deploy over a problem that does not exist. A host that declares nothing is skipped and logged as skipped. Declarations are the 2026-08-25 measurements: fw13 and astra pop/24.04 glibc 2.39, prod ubuntu/24.04 glibc 2.39, testnot ubuntu/26.04 glibc 2.43. The last is the finding worth reading twice: staging is two Ubuntu releases and four glibc point versions ahead of production. Not done here: comparing a build host's libc against a deploy node's. That is the real compatibility question, it still happens at runtime in the ldd guard, and hoisting it to preflight is filed separately rather than smuggled in.
Author: Max Johnson <me@maxj.phd> · 2026-08-25 13:37 UTC
Signed with PGP, not checked
Commit: 4b34092ebe0648910bdf4d6578794eac2a2cc82b
Parent: 21eac73
18 files changed, +817 insertions, -154 deletions
@@ -3552,7 +3552,7 @@
3552 3552
3553 3553 [[package]]
3554 3554 name = "tagtree"
3555 - version = "0.4.0"
3555 + version = "0.4.1"
3556 3556
3557 3557 [[package]]
3558 3558 name = "tendril"
@@ -4865,27 +4865,27 @@
4865 4865
4866 4866 [[patch.unused]]
4867 4867 name = "quasi-axum"
4868 - version = "0.44.0"
4868 + version = "0.56.0"
4869 4869
4870 4870 [[patch.unused]]
4871 4871 name = "quasi-basics"
4872 - version = "0.44.0"
4872 + version = "0.56.0"
4873 4873
4874 4874 [[patch.unused]]
4875 4875 name = "quasi-http"
4876 - version = "0.44.0"
4876 + version = "0.56.0"
4877 4877
4878 4878 [[patch.unused]]
4879 4879 name = "quasi-immediate"
4880 - version = "0.44.0"
4880 + version = "0.56.0"
4881 4881
4882 4882 [[patch.unused]]
4883 4883 name = "quasi-notifs"
4884 - version = "0.44.0"
4884 + version = "0.56.0"
4885 4885
4886 4886 [[patch.unused]]
4887 4887 name = "quasi-router"
4888 - version = "0.44.0"
4888 + version = "0.56.0"
4889 4889
4890 4890 [[patch.unused]]
4891 4891 name = "quasi-store"
@@ -4893,15 +4893,19 @@
4893 4893
4894 4894 [[patch.unused]]
4895 4895 name = "quasi-tauri"
4896 - version = "0.44.0"
4896 + version = "0.56.0"
4897 4897
4898 4898 [[patch.unused]]
4899 4899 name = "quasi-webview"
4900 - version = "0.44.0"
4900 + version = "0.56.0"
4901 +
4902 + [[patch.unused]]
4903 + name = "quasi-type"
4904 + version = "0.1.0"
4901 4905
4902 4906 [[patch.unused]]
4903 4907 name = "synckit-client"
4904 - version = "0.8.0"
4908 + version = "0.9.0"
4905 4909
4906 4910 [[patch.unused]]
4907 4911 name = "synckit-config"
@@ -4918,31 +4922,3 @@
4918 4922 [[patch.unused]]
4919 4923 name = "painhours"
4920 4924 version = "0.1.0"
4921 -
4922 - [[patch.unused]]
4923 - name = "quasi-type"
4924 - version = "0.1.0"
4925 -
4926 - [[patch.unused]]
4927 - name = "makeover-build"
4928 - version = "0.47.0"
4929 -
4930 - [[patch.unused]]
4931 - name = "makeover-immediate"
4932 - version = "0.30.0"
4933 -
4934 - [[patch.unused]]
4935 - name = "makeover-layout"
4936 - version = "0.31.0"
4937 -
4938 - [[patch.unused]]
4939 - name = "makeover-touch"
4940 - version = "0.22.0"
4941 -
4942 - [[patch.unused]]
4943 - name = "makeover-tui"
4944 - version = "0.30.0"
4945 -
4946 - [[patch.unused]]
4947 - name = "makeover-webview"
4948 - version = "0.52.0"
M pom/Cargo.lock +4 -4
@@ -3601,6 +3601,10 @@
3601 3601 source = "registry+https://github.com/rust-lang/crates.io-index"
3602 3602 checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
3603 3603
3604 + [[patch.unused]]
3605 + name = "docengine"
3606 + version = "0.7.0"
3607 +
3604 3608 [[patch.unused]]
3605 3609 name = "quasi-axum"
3606 3610 version = "0.56.0"
@@ -3641,10 +3645,6 @@
3641 3645 name = "quasi-type"
3642 3646 version = "0.1.0"
3643 3647
3644 - [[patch.unused]]
3645 - name = "docengine"
3646 - version = "0.7.0"
3647 -
3648 3648 [[patch.unused]]
3649 3649 name = "kberg"
3650 3650 version = "0.1.0"
M sando/Cargo.lock +28 -51
@@ -999,7 +999,7 @@
999 999
1000 1000 [[package]]
1001 1001 name = "ops-core"
1002 - version = "0.1.0"
1002 + version = "0.2.0"
1003 1003 dependencies = [
1004 1004 "anyhow",
1005 1005 "async-trait",
@@ -1007,6 +1007,7 @@
1007 1007 "ops-exec",
1008 1008 "serde",
1009 1009 "sqlx",
1010 + "thiserror",
1010 1011 "tokio",
1011 1012 "tracing",
1012 1013 ]
@@ -1227,7 +1228,7 @@
1227 1228
1228 1229 [[package]]
1229 1230 name = "sando-daemon"
1230 - version = "0.2.10"
1231 + version = "0.2.11"
1231 1232 dependencies = [
1232 1233 "anyhow",
1233 1234 "async-trait",
@@ -2267,27 +2268,27 @@
2267 2268
2268 2269 [[patch.unused]]
2269 2270 name = "quasi-axum"
2270 - version = "0.46.0"
2271 + version = "0.56.0"
2271 2272
2272 2273 [[patch.unused]]
2273 2274 name = "quasi-basics"
2274 - version = "0.46.0"
2275 + version = "0.56.0"
2275 2276
2276 2277 [[patch.unused]]
2277 2278 name = "quasi-http"
2278 - version = "0.46.0"
2279 + version = "0.56.0"
2279 2280
2280 2281 [[patch.unused]]
2281 2282 name = "quasi-immediate"
2282 - version = "0.46.0"
2283 + version = "0.56.0"
2283 2284
2284 2285 [[patch.unused]]
2285 2286 name = "quasi-notifs"
2286 - version = "0.46.0"
2287 + version = "0.56.0"
2287 2288
2288 2289 [[patch.unused]]
2289 2290 name = "quasi-router"
2290 - version = "0.46.0"
2291 + version = "0.56.0"
2291 2292
2292 2293 [[patch.unused]]
2293 2294 name = "quasi-store"
@@ -2295,51 +2296,11 @@
2295 2296
2296 2297 [[patch.unused]]
2297 2298 name = "quasi-tauri"
2298 - version = "0.46.0"
2299 + version = "0.56.0"
2299 2300
2300 2301 [[patch.unused]]
2301 2302 name = "quasi-webview"
2302 - version = "0.46.0"
2303 -
2304 - [[patch.unused]]
2305 - name = "quasi-type"
2306 - version = "0.1.0"
2307 -
2308 - [[patch.unused]]
2309 - name = "synckit-client"
2310 - version = "0.8.1"
2311 -
2312 - [[patch.unused]]
2313 - name = "synckit-config"
2314 - version = "0.2.0"
2315 -
2316 - [[patch.unused]]
2317 - name = "makeover-build"
2318 - version = "0.48.0"
2319 -
2320 - [[patch.unused]]
2321 - name = "makeover-immediate"
2322 - version = "0.31.0"
2323 -
2324 - [[patch.unused]]
2325 - name = "makeover-layout"
2326 - version = "0.32.0"
2327 -
2328 - [[patch.unused]]
2329 - name = "makeover-touch"
2330 - version = "0.22.1"
2331 -
2332 - [[patch.unused]]
2333 - name = "makeover-tui"
2334 - version = "0.31.0"
2335 -
2336 - [[patch.unused]]
2337 - name = "makeover-webview"
2338 - version = "0.54.0"
2339 -
2340 - [[patch.unused]]
2341 - name = "docengine"
2342 - version = "0.7.0"
2303 + version = "0.56.0"
2343 2304
2344 2305 [[patch.unused]]
2345 2306 name = "kberg"
@@ -2351,4 +2312,20 @@
2351 2312
2352 2313 [[patch.unused]]
2353 2314 name = "tagtree"
2354 - version = "0.4.0"
2315 + version = "0.4.1"
2316 +
2317 + [[patch.unused]]
2318 + name = "quasi-type"
2319 + version = "0.1.0"
2320 +
2321 + [[patch.unused]]
2322 + name = "synckit-client"
2323 + version = "0.9.1"
2324 +
2325 + [[patch.unused]]
2326 + name = "synckit-config"
2327 + version = "0.2.0"
2328 +
2329 + [[patch.unused]]
2330 + name = "docengine"
2331 + version = "0.7.0"
@@ -134,6 +134,13 @@
134 134 ssh_target = "deploy@testnot" # tailnet name; never the public IP / testnot.work hostname
135 135 release_root = "/opt/mnw"
136 136 service_name = "makenotwork.service"
137 + # What this box IS, checked against it before anything is pushed (2026-08-25).
138 + # Note it is NOT the same base as prod-1 below: staging is two Ubuntu releases
139 + # and four glibc point versions ahead, so tier A does not rehearse tier B on the
140 + # one axis this field exists for. That is a real gap, tracked separately; the
141 + # declaration states it rather than hiding it.
142 + base_image = "ubuntu/26.04"
143 + libc = "2.43"
137 144 # Pre-swap config-drift guard: source this node's env and run the freshly-rsynced
138 145 # binary in MNW_CHECK_CONFIG=1 mode BEFORE the symlink swap. A required var
139 146 # missing here (how prod crash-looped on CDN_BASE_URL, postmortem 2026-07-09 #2)
@@ -190,6 +197,12 @@
190 197 ssh_target = "makenotwork@alpha-west-1"
191 198 release_root = "/opt/mnw"
192 199 service_name = "makenotwork.service"
200 + # Measured 2026-08-25. The margin here is zero: makenotwork, mnw-cli and pom all
201 + # carry a GLIBC_2.39 floor and this box has exactly 2.39, so one point release of
202 + # drift on the build host breaks it. Declaring the number is what lets that be
203 + # compared before a build rather than by the pre-swap `ldd` guard afterwards.
204 + base_image = "ubuntu/24.04"
205 + libc = "2.39"
193 206 # Pre-swap config-drift guard (see testnot-1 above). This node is the one the
194 207 # 0.10.14 deploy crash-looped on a missing CDN_BASE_URL — the exact miss this
195 208 # gate closes. First real exercise is the next prod promote.
@@ -10752,6 +10752,14 @@
10752 10752 name = "painhours"
10753 10753 version = "0.1.0"
10754 10754
10755 + [[patch.unused]]
10756 + name = "synckit-client"
10757 + version = "0.9.0"
10758 +
10759 + [[patch.unused]]
10760 + name = "synckit-config"
10761 + version = "0.2.0"
10762 +
10755 10763 [[patch.unused]]
10756 10764 name = "quasi-immediate"
10757 10765 version = "0.56.0"
@@ -10767,11 +10775,3 @@
10767 10775 [[patch.unused]]
10768 10776 name = "quasi-tauri"
10769 10777 version = "0.56.0"
10770 -
10771 - [[patch.unused]]
10772 - name = "synckit-client"
10773 - version = "0.9.0"
10774 -
10775 - [[patch.unused]]
10776 - name = "synckit-config"
10777 - version = "0.2.0"
@@ -153,7 +153,7 @@
153 153
154 154 [[package]]
155 155 name = "bento-daemon"
156 - version = "0.1.0"
156 + version = "0.1.1"
157 157 dependencies = [
158 158 "anyhow",
159 159 "async-trait",
@@ -1346,7 +1346,7 @@
1346 1346
1347 1347 [[package]]
1348 1348 name = "ops-core"
1349 - version = "0.1.0"
1349 + version = "0.2.0"
1350 1350 dependencies = [
1351 1351 "anyhow",
1352 1352 "async-trait",
@@ -1354,6 +1354,7 @@
1354 1354 "ops-exec",
1355 1355 "serde",
1356 1356 "sqlx",
1357 + "thiserror",
1357 1358 "tokio",
1358 1359 "tracing",
1359 1360 ]
@@ -3300,25 +3301,29 @@
3300 3301 source = "registry+https://github.com/rust-lang/crates.io-index"
3301 3302 checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
3302 3303
3303 - [[patch.unused]]
3304 - name = "synckit-client"
3305 - version = "0.8.0"
3306 -
3307 - [[patch.unused]]
3308 - name = "synckit-config"
3309 - version = "0.2.0"
3310 -
3311 3304 [[patch.unused]]
3312 3305 name = "quasi-axum"
3313 - version = "0.1.0"
3306 + version = "0.56.0"
3307 +
3308 + [[patch.unused]]
3309 + name = "quasi-basics"
3310 + version = "0.56.0"
3314 3311
3315 3312 [[patch.unused]]
3316 3313 name = "quasi-http"
3317 - version = "0.1.0"
3314 + version = "0.56.0"
3315 +
3316 + [[patch.unused]]
3317 + name = "quasi-immediate"
3318 + version = "0.56.0"
3319 +
3320 + [[patch.unused]]
3321 + name = "quasi-notifs"
3322 + version = "0.56.0"
3318 3323
3319 3324 [[patch.unused]]
3320 3325 name = "quasi-router"
3321 - version = "0.1.0"
3326 + version = "0.56.0"
3322 3327
3323 3328 [[patch.unused]]
3324 3329 name = "quasi-store"
@@ -3326,40 +3331,16 @@
3326 3331
3327 3332 [[patch.unused]]
3328 3333 name = "quasi-tauri"
3329 - version = "0.1.0"
3334 + version = "0.56.0"
3330 3335
3331 3336 [[patch.unused]]
3332 3337 name = "quasi-webview"
3333 - version = "0.1.0"
3338 + version = "0.56.0"
3334 3339
3335 3340 [[patch.unused]]
3336 3341 name = "docengine"
3337 3342 version = "0.7.0"
3338 3343
3339 - [[patch.unused]]
3340 - name = "makeover-build"
3341 - version = "0.26.0"
3342 -
3343 - [[patch.unused]]
3344 - name = "makeover-immediate"
3345 - version = "0.15.0"
3346 -
3347 - [[patch.unused]]
3348 - name = "makeover-layout"
3349 - version = "0.17.0"
3350 -
3351 - [[patch.unused]]
3352 - name = "makeover-touch"
3353 - version = "0.10.0"
3354 -
3355 - [[patch.unused]]
3356 - name = "makeover-tui"
3357 - version = "0.16.1"
3358 -
3359 - [[patch.unused]]
3360 - name = "makeover-webview"
3361 - version = "0.33.0"
3362 -
3363 3344 [[patch.unused]]
3364 3345 name = "kberg"
3365 3346 version = "0.1.0"
@@ -3370,4 +3351,16 @@
3370 3351
3371 3352 [[patch.unused]]
3372 3353 name = "tagtree"
3373 - version = "0.4.0"
3354 + version = "0.4.1"
3355 +
3356 + [[patch.unused]]
3357 + name = "synckit-client"
3358 + version = "0.9.1"
3359 +
3360 + [[patch.unused]]
3361 + name = "synckit-config"
3362 + version = "0.2.0"
3363 +
3364 + [[patch.unused]]
3365 + name = "quasi-type"
3366 + version = "0.1.0"
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "bento-daemon"
3 - version = "0.1.0"
3 + version = "0.1.1"
4 4 edition = "2024"
5 5 license = "MIT"
6 6
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "sando-daemon"
3 - version = "0.2.10"
3 + version = "0.2.11"
4 4 edition = "2024"
5 5 license = "MIT"
6 6
@@ -808,6 +808,7 @@
808 808 "serde_json",
809 809 "sqlx",
810 810 "tempfile",
811 + "thiserror",
811 812 "tokio",
812 813 "tracing",
813 814 ]
@@ -1865,26 +1866,70 @@
1865 1866 source = "registry+https://github.com/rust-lang/crates.io-index"
1866 1867 checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
1867 1868
1868 - [[patch.unused]]
1869 - name = "synckit-client"
1870 - version = "0.8.0"
1871 -
1872 - [[patch.unused]]
1873 - name = "synckit-config"
1874 - version = "0.2.0"
1875 -
1876 1869 [[patch.unused]]
1877 1870 name = "kberg"
1878 1871 version = "0.1.0"
1879 1872
1873 + [[patch.unused]]
1874 + name = "ops-status"
1875 + version = "0.1.0"
1876 +
1880 1877 [[patch.unused]]
1881 1878 name = "painhours"
1882 1879 version = "0.1.0"
1883 1880
1884 1881 [[patch.unused]]
1885 1882 name = "tagtree"
1886 - version = "0.4.0"
1883 + version = "0.4.1"
1884 +
1885 + [[patch.unused]]
1886 + name = "synckit-client"
1887 + version = "0.9.1"
1888 +
1889 + [[patch.unused]]
1890 + name = "synckit-config"
1891 + version = "0.2.0"
1892 +
1893 + [[patch.unused]]
1894 + name = "quasi-type"
1895 + version = "0.1.0"
1896 +
1897 + [[patch.unused]]
1898 + name = "quasi-axum"
1899 + version = "0.56.0"
1900 +
1901 + [[patch.unused]]
1902 + name = "quasi-basics"
1903 + version = "0.56.0"
1904 +
1905 + [[patch.unused]]
1906 + name = "quasi-http"
1907 + version = "0.56.0"
1908 +
1909 + [[patch.unused]]
1910 + name = "quasi-immediate"
1911 + version = "0.56.0"
1912 +
1913 + [[patch.unused]]
1914 + name = "quasi-notifs"
1915 + version = "0.56.0"
1916 +
1917 + [[patch.unused]]
1918 + name = "quasi-router"
1919 + version = "0.56.0"
1920 +
1921 + [[patch.unused]]
1922 + name = "quasi-store"
1923 + version = "0.1.0"
1924 +
1925 + [[patch.unused]]
1926 + name = "quasi-tauri"
1927 + version = "0.56.0"
1928 +
1929 + [[patch.unused]]
1930 + name = "quasi-webview"
1931 + version = "0.56.0"
1887 1932
1888 1933 [[patch.unused]]
1889 1934 name = "docengine"
1890 - version = "0.4.0"
1935 + version = "0.7.0"