J470: add a Mac node from a production build (release darwin binary, infraternal up on macOS) #483
Open
opened 2026-11-10 05:22:52 +00:00 by kayg
·
16 comments
No Branch/Tag specified
main
j470b-server-mesh-rejoin
mesh-compat-382
j444b-d381-key-wins-flood
j461b-macos-utun-reader-thread
j475b-remove-host-motion
fix-reconnect-new-link
j481-d380
dev
j479-mesh-soak
fix/j476-host-facts
j475-motion-pill-split
j470-mac-node-join
root-upgrade-d376
j474-review-merge
latency-targets-120hz
nightly-suite-shards
gotatun-pin-j469
macos-utun-reader-thread
fix-flood-echo-stall
wip/j416
motion-fast-ease-out
mesh-addhost-j259
takeover-close-terminals-d368
kbd-instant-followups
fix-trust-key-double-click
nightly-suite-partial-report
appwin-icons-j207
rusqlite-slice1
swipe-content-first-d374
mesh-mtu-381
addhost-instant-open
glass-contrast-d365-mock
glass-contrast-d365
signout-followups-442
fix-startup-toast-red
research-gotatun-matrix-j317
themes-sync-j304
fix-load-flakes
fix-nix-zig-patch
fix-compute01-browser-baseline
ratchet-size-baseline-419
predict-auto-d130
revoke-coverage-d367
nightly-suite-queue-timeout
nightly-browser-suite
fix-rustsec-2026-0459
fonts-ioskeley-obel
log-retention-d375
fix-kbd-no-anim-427
fix-dev-red-specs-2
takeover-proof-305-j205
wip/j410-server-pump
ui-consistency-d372
fix-osc52-clipboard
libghostty-vt-1.0
forget-retired-host-d369
j404-merge-426
fix-setup-lock-race
fix-touch-select-426
revoke-access-report-ui
fix-predictive-echo-specs
fix-selinux-agent-exec
wip/j395-checkpoint2
wip/j395
j391-review-scratch
ui-consistency-j195-deferred-rebased
fix-rustc-paths-421
decisions-wording-341
fix-install-suite-red
fix-dev-rtt-specs
fix-pending-removal-align
fix-privileged-skip
fix-install-suite-404
review-j368
mesh-dns-j254
revoke-access-report-d356b
revoke-privilege-round5
fix-removal-confirm
review-j360
gotatun-rotation-367
deps-refresh-340
review-j354
review-J351
review-j350
addhost-takeover-d359
revoke-report-items-347
revoke-end-sessions-d362
review-j348
size-mesh-ssh-346
size-trim-337
revoke-access-diff-d356
j259-pre-rebase-b06c5a6
j305-ended-at-start-spec
addhost-sudo-consent
fix-layout-real-specs-411
fix-shell-open-411
fix-console-hosts-373
fix-shell-exit
fuzz-host-name-353
fix-host-details-410
macos-filter-j197
research-gotatun-tricks
revoke-p3-j216l
stale-state-pill
revoke-followups-j216k
revoke-progress-j216e
backup/macos01-20261106/lume/j197
backup/macos01-20261106/lume/appwin-icons-j207-stash0
backup/macos01-20261106/claude/stash-0
backup/macos01-20261106/claude/stash-1
backup/macos01-20261106/claude/J80-macos-s1
backup/macos01-20261106/claude/J80
backup/macos01-20261106/claude/J80-slice1-20261102
backup/macos01-20261106/claude/J16b-appwin-on-dev
backup/macos01-20261106/claude/appwin-launch-idle-146
backup/macos01-20261106/claude/J80-s1-run
wip/j259
trust-285-p3c
runtime-lock-j216f
mesh-ssh-p3-j175
tun-proof-p3b
trust-285-p3
terminal-polish-j271
tun-proof-p3
ssh-install-flakes-j216i
fuzz-fixes-j264
trust-285-a
trust-285-b
restart-oversized-fixture-j198
backup/macos01-20261106/lume/appwin-icons-j207
restart-bound-j216h
up-preflight-p3-j198
backup/macos01-20261106/lume/j251
up-preflight-j198
wip/up-preflight-j198-j245-p2
wip/up-preflight-j198-j245-p1
agent-fsync-offloop-j196
wip/up-preflight-j198-j245
settings-offscreen-j231
wip/up-preflight-j198-j226-inspection
wip/j103f
themes-j206
wip/up-preflight-j198-j226
flakes-j216c
backup/macos01-20261106/claude/j197
flakes-j216b
backup/macos01-20261106/claude/j197-red
wip/up-preflight-j198-review
flakes-j216
backup/macos01-20261106/claude/appwin-icons-j207
engineering-j208b
appwin-mock-j209
toast-stack-j165
engineering-j208
decisions-d351
decisions-d350
decisions-d349
appearance-presets-mock-j129
decisions-d348
docs-audit-spec
doc-coverage-baseline-raise
decisions-d347
decisions-d346b
decisions-d346
j88h-profile
ui-consistency-j195-deferred
ui-consistency-j195
engineering-practices-j194
preferred-login-ui-j166
signin-pools-263-j174
macos-node-265-s1
decisions-d345
decisions-d344
proxy-cgroup-j164
verify-quick-fuzz-329
proxy-start-io-j168
timing-flakes-j163
up-263-s1
mcp-events-j147
verify-quick-fuzz-j158
proxy-p3-j159
agent-resource-j157-detect
wip/j103d-final
remote-cancel-hang-j155
proxy-traefik-281-j62d
j149-verify-quick
proxy-traefik-281-j62b
j150-drop-unnamed
wip/j79d-red-r4
wip/j103d-d332
rootless-toast-j142
wip/j103d-head
wip/j103d
j125c-mcp
macos-build-j136
backup/macos01-20261106/claude/j136
wip/j79d-red-cli
wip/j79d-red-server
j121-link-liveness
j118c-tracer-failures
wip/j79c-head
release-desktop-j131
j124c-signed-macos
wip/j79c-red-p3
wip/j79c-red-p23
wip/j79c-red-p22
merge-j128-check
perf-deferred-j128
sidebar-toggle-260
j127-shortcut-changed-dot
j125-mcp
j118-tracer-failures
j126-account-summary
settings-sections-mock-j92
j120-pinch-lock
sidebar-292
backup/macos01-20261106/claude/j120
taste-2026-11-03
row-actions-242-215
backup/macos01-20261106/claude/stash-0-untracked
backup/macos01-20261106/claude/J80-265-s1-run
backup/macos01-20261106/claude/J80-macos-s1-20261102
j112-canvas-commands
ui-no-overshoot-scroll
fix-recovery-restart
backup/macos01-20261106/claude/stash-1-untracked
taste-2026-11-02
size-baseline-ratchet
machine-cards-structure-j108
j109-boot-copy-mockups
fix-dev-red
wip/up-263-s1
j95b-settings-rows
proxy-traefik-281
j95-option-rows
docs-human-j91
docs-keys-palette
fix-flood-1013-270-j38d
mesh-ssh-s1
mesh-ssh-s0
addhost-d291-tunnel
test-stability-load
mesh-relay-220
repro-296
shim-thp-off-24
mesh-pins-258-v2
mesh-dns
signin-d311-merged
docs-pages
ui-size-283
fix-ui-batch-specs
wip/j12b-edge-update
gate-on-arm
fix-rename-sync-293
trust-285-j60b
fix-focus-ring-clip
integ-ui-1101
edge-update-v2
signin-d311
taste-2026-11-01
layout-per-tab-250
ui-approved-splitopt-224
ui-approved-splitopt-224-on-dev
icon-text-centre-282
ui-approved-189-212-216
research-reverse-proxy
transfer-budget-320
review-mesh-pins-258-r2
sidebar-pill-259
review-mesh-relay-220
wip/j12-edge-update-v2
nix-package
status-crash-loop-237
fix-dev-test-failures
fix-fixture-socket-path
wip/mesh-relay-220-pre-dev-advance-rebase
fix-canvas-side-clip
research-head-to-head
frosted-real-256
review-mesh-pins-258
wip/mesh-relay-220-pre-final-rebase
review-layout-per-tab-250-r2
broad-suite-triage
nix-devshell
review-status-crash-loop-237-r3
wip/nix-devshell-pre-p3
taste-frosted-real
wip/code-serve-demo-2026-10-30
wip/code-serve-glass-2026-10-30
wip/code-serve-clusters-2026-10-30
wip/code-merge-release94-dev-2026-10-30
wip/rproxy-delta-scratch-2026-10-30
wip/stale-note-257-staged-2026-10-30
wip/d241-size-staged-2026-10-30
review-broad-suite-207
wip/mesh-pins-258-pre-split
wip/mesh-relay-220-pre-rebase
mesh-measure-220
plan-211-update
review-edge-autoupdate-r3
perf24-reconnect
review-stale-note-r2
release-prod-live
d241-shell-marker
review-mesh-pins-server-258
review-revocation-r3
review-setup-token-r2
review-sync-journal-r3
wip/review-mesh-pins-258-prune-review-1995fee46
frosted-scroll-256
sync-journal-r3
revocation-per-account-wip
review-tokio-asyncfd-0386
review-sync-p3s
wip/stash-8by64-stash0-2026-10-30
mesh-drag-r2
setup-token-data-wip
release-9-4
auth-busy-1013
mesh-view-overlap
sync-p3s
review-auth-busy-wake
review-readd-r8-wip
mesh-drag-bounds
audit-auth-93
app-windows-stream-start
review-authority-cap-r3
app-windows-r7-measure
readd-minimal-r6
readd-minimal-r5
app-windows-r7-soak
readd-minimal-r4
app-windows-r7
app-windows-r6
readd-minimal-r3
readd-minimal-r2
readd-minimal
merge-release8-dev
review-busy-page-liveness-r2
review-busy-page-liveness
mesh-view-trim
review-authority-read-retry-r2
review-remove-uninstall-r2
release-retry-reattach
release-retry
review-remove-uninstall
review-authority-read-retry
app-windows-ship
mesh-autoswitch-p2
mesh-ssh
mesh-relay
mesh-macos-node
screen-and-control
session-labels
review-real-logins-r5
review-real-logins-r4
review-real-logins-r3
shim-memory-r2
review-real-logins-r2
shim-memory
shadow-addhost-instant
shadow-real-logins
shadow-same-session-flood
shadow-prod-perf-fix
hosts-from-server
app-windows-real
app-windows-transport
release-prod-6w
mesh-policy-verify
quiet-close-toast
dev-merge-r4
review-mesh-agent-transport
review-app-windows-transport
settings-roundtrip
review-release-pipeline-r4
mesh-agent-transport
ended-strip-fix
review-audit-ca-update-d244
settings-rows
phone-width
signout-clear
fix-restart-tests
dev-merge-rp
signer-hardening
review-audit-ca-update
terminal-fidelity
review-audit-ca-update-wip
mesh-integrate
review-mesh-agent-transport-515d3712
review-mesh-dataplane-feec1988
mesh-dataplane
signout
research-canvas-node
wip/review-renderer-cost
wip/review-terminal-fidelity
padlock-glyph
review-release-pipeline-2e95b689
phone-polish
sidebar-polish
inbox-generic
review-mesh-dataplane-378d4055
prod-batch
ui-inbox
ui-ca-unlock
ui-machine-sections
review-mesh-dataplane-a5f744fb
tls-session-prod
session-bounds-prod
fix-dead-session
session-bounds
tls-session
palette-polish
shortcuts-d222
research-appmeta
phone-gutter
ui-centre-button
wip/ui-centre-button
wip/tls-session
review-addhost-breakit
ui-platform
renderer-cost
remote-agents
mesh-api
ssh-bootstrap
prod-hotfix2
prod-fixes
review-remote-agents
review-mesh-dataplane
session-life
research-browser
review-mesh-api
research-capture
review-mesh-api-round3
review-mesh-api-round1
gate-stability
review-gate-stability
review-relay-direct
review-mesh-api-findings
proto-sessions
font-picker
proto-addhost
proto-windows
mesh-core
latency-gate
research-bsd
research-discovery
keytray-mods
mock-core
ui-tweaks
fix-motion-sel
fix-motion
integrate2
fix-robust
t-web
t-server
vt-ghostty
fix-snap-echo
t-web-auth
review-vt2
t-agent
lane-protocol
lane-shim
lane-auth
s1-base
s1-contracts
s1-workspace
No results found.
No labels
launch-qa
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
kayg/infraternal#483
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Owner asked (2026-11-10): "so can i finally add a mac node?" Today the answer from production is no. J470 makes "add a Mac" work end to end from a production-style build.
Branch
j470-mac-node-join(from origin/dev3c0b20252). Related: #265 (macOS mesh node), #383 (Mac status), #379 (Mac build), #321 (signed Mac CLI), D258, D311, D352, D378.Gap map: where each path stops for a Mac (dev
3c0b20252)curl -fsSL https://<server>/install | sh(D258/D311)/installroute at all, for Linux either. The server routes are/auth,/remote/*,/v1/*,/healthz. There is no install script and no join-code issuer that the script could read from.infraternal up <server>on the Macpackage-release/release/build/publishship onlyinfraternal{,-x86_64,-aarch64}-muslplus session images.verify_downloadrefuses any other name in the manifest. 2.upis Linux-only.dispatch_up_action,run_upandrun_up_systemarecfg(all(remote, target_os = "linux")), so on a Macupreports "unavailable". The Linux root helper is systemd-only (up_install.rsjournal,/proc/self/exe,systemctl).crates/plugins/ssh/src/probe.rs) accepts onlyLinuxwith systemd and refuses Darwin. The payload resolver (release_payload.rs) knows onlyinfraternal-<arch>-musl. The installer writes systemd units.What already works on dev for a Mac: the agent builds on aarch64-darwin (
scripts/macos-build-check). The mesh node is GotaTun on utun (macos_node.rs).infraternal mesh upinstalls the root LaunchDaemonorg.infraternal.mesh, which runsagent run --data /var/lib/infraternal/agent --state /var/lib/infraternal/state(macos_service.rs). An enrolled Mac then meshes; J267 did this by hand (agent join --enroll-only, thenmesh up).Signing: the release-signer Worker (
apps/release-signer/src/index.js) hashes and signs every top-level file underreleases/<version>/. The server's Rust verifier (release_verify.rs) accepts extra names. So a darwin binary needs no new key and no Worker change, only the bundle, publish andverify_downloadlists.Shortest path: (b) first
scripts/release/build-darwinruns on the Mac with the OS toolchain (Apple SDK,/usr/bin/clang; Linux cannot cross-link it, see #379). It is release-stamped with the same version as the Linux build and ad-hoc codesigned.package-release/buildtake that binary asinfraternal-aarch64-darwin, andpublishandverify_downloadcarry it. It goes into SHA256SUMS, so the Worker signs it. x86_64-darwin is skipped: it needs an x86 toolchain build on the arm Mac, and the owner has no Intel Mac in scope.infraternal upon macOS: the same device approval as Linux (link, code and QR on the terminal; nothing in argv). Then one sudo, which runs a macOS root helper. The helper copies the running binary to/var/lib/infraternal/bin/infraternal, enrolls into/var/lib/infraternal/agentand installs theorg.infraternal.meshLaunchDaemon (existing code). If enrollment fails, it rolls back what it created.infraternal-macos-01-stdagainst a throwaway server on compute01 (never prod). The Mac joins, is listed and opens a terminal. Mesh ping plus a bulk transfer, both ways, with a Linux node. Screenshots from a production build.scripts/verify faston compute01, plusscripts/macos-build-checkon the Mac.Deliberate deviation from the brief, decided under D352 (Mac parity): the brief says "the rootless agent under launchd as the user". On dev the Mac agent and the mesh peer are one process, the root LaunchDaemon above. That is the same shape as Linux
up, which installs a root systemd service. Splitting it into a user LaunchAgent plus a root mesh daemon would be a redesign of the mesh plugin's process boundary, so it is out of scope here. If the owner wants the split, it gets its own issue.Later slices (not in J470): (a) needs a server join-code issuer, an
/installroute and a script with the release key embedded (D311). (c) needs a Darwin probe, a launchd installer over SSH and a darwin payload inrelease_payload.rs.Estimate: about 6 h. The Mac release build is about 35 min per run (J267), and the Mac lock is shared.
J470 progress (~15:30 IST). Branch
j470-mac-node-join, head815f6bae2, base origin/dev3c0b20252. Pushed.Commits:
7903c1de9up:infraternal upjoins a Mac and installs its launchd service. The device approval is shared with Linux. One sudo runs__up-system, which copies the binary to/var/lib/infraternal/bin, enrolls into/var/lib/infraternal/agent, adds the/usr/local/bin/infraternalandinfralinks, and installs theorg.infraternal.meshLaunchDaemon. If anything fails before launchd reports the job running, it rolls back by inode. The command, its dispatch and its module are now gated on a newcfg(machine_up)(remote, plus Linux or macOS with mesh).815f6bae2release: the macOS arm64 binary ships in signed releases. That addsscripts/release/build-darwin(on the Mac),scripts/release/build --darwin DIR, and darwin support in publish and verify. The release tests pass locally (11, including the new darwin cases).Findings so far:
qrcodegenwas a Linux-only target dependency, andpermission_errorwas gated too narrowly. Both are fixed. Linuxverify quickwould never have seen this, which is the #379 lesson again.df63e43dd:BUILD_EXIT=0 seconds=190, versionedge-20261110T094343Z, sha256242f1b3f…. Log:claude@macos-01:~/j470-evidence/build-2.log.upinstall on dev-02, taken under the live-mesh claim and removed afterwards. The bench VM .7 is J461's live target, so I'm leaving it alone.releases/channels/edge.jsonreturns 404), so nothing published can be reused. I'm building a production-style bundle on compute01 withrelease/build --channel edge --darwin.Next: rebuild the Mac binary at the head, run quick, build the bundle, then the live proof.
J470: live proof results and a blocker (~17:40 IST). Head
70819f4f1, pushed.What works, from a production-style build
scripts/release/build --channel edge --darwinon compute01 givesBUNDLE_EXIT=0 seconds=2878, versionedge-20261110T100622Z.sha256sum --strict --check SHA256SUMSis OK on 23 files. It includesinfraternal-aarch64-darwin(sha2563813403260cd…, 23,179,392 B), next toinfraternal-x86_64-musl(11,468,808 B) andinfraternal-aarch64-musl(10,612,688 B). Not published: publishing and signing are the owner's.build-darwintakes 134–190 s on the Mac. It is ad-hoc signed with the hardened runtime (flags=0x10002(adhoc,runtime),Identifier=org.infraternal.cli).otool -Lshows only/System/Libraryand/usr/lib. Re-signing the same bytes gives the same sha256 (07d3c1cf…both times), so the ad-hoc signature is reproducible.infraternal up https://test.ssh.kayg.orgon macos-01 (aslume, in a pty) printed the link, code, QR code and machine key. I approved it through the real approval page with the test admin passkey (code passed on stdin, never argv). Result:This machine joined the server and its system service is active.,UP_EXIT=0./var/lib/infraternal/{bin/infraternal,agent/identity.json,state}(root, 0711/0700/0755) and the/usr/local/bin/{infraternal,infra}links.org.infraternal.mesh:state = running.Infraternal Mesh daemon restore complete active=true.A bug found and fixed (commit
70819f4f1)The first live
upfailed withCould not reach the server over HTTPS(exit 5), while curl worked. The vendored OpenSSL's default path is the build prefix, and our fallback bundle list named only Linux files, so no public root was ever loaded on macOS. The fix is one shared list,infraternal_plugin_api::tls_roots, which adds/etc/ssl/cert.pemafter the Linux names. It replaces five copies of the list: the agent websocket, CLI enrollment and removal, the server release download, and two tests. Without it, no Mac agent could reach any server with a public certificate.Blocked: list, terminal and mesh on the test server
Mesh · 0 hosts, and Console shows onlyssh. Screenshots:v1-picker.png,mac-list.png. The Mac's mesh state is"config": null(/var/lib/infraternal/state/mesh-public.json), so the server never sent it a mesh config. The Mac's host id iseb5e375e71d642c699514a3b5cd74bc3. I can't read the test server's logs or database from a job, so I can't tell whetherup-joined (hand-joined) hosts are hidden by design or by a bug, or whether this server runs no mesh coordinator.uppeer. dev-02'supcorrectly refuses (Service infraternal-browser-suite.service invokes infraternal), and so does compute01 (An infraternal server is running on this host).Question for the orchestrator:
eb5e375e…in theinfraternal-testjournal/DB, and say whether test.ssh.kayg.org has a mesh coordinator?server.shpattern, run as root so the server's own mesh comes up), with a clean Linux peer in a container or a fresh VM.Safe default meanwhile: I'm running the gates, then uninstalling the Mac test install and restoring what I found there. A reinstall from the bundle takes about a minute.
J470: (b) answered, and a new finding (~19:15 IST)
(b) The host list is not a bug; my proof script was wrong
I recorded the websocket frames the test UI exchanges. The server answers
remote.host.listwith the Mac:It was
online:falseonly because I had already uninstalled the Mac. The client'spage()accepts nullosandaddress, andreplaceHostskeeps the host. Remote hosts are reached through the palette: Ctrl+Shift+K, "macos" givesOpen shell on infraternal-macos-01-stdunder Console andRemove host: infraternal-macos-01-stdunder Hosts (screenshot attached). The sidebar's Console row lists open windows, not hosts. "Mesh · 0 hosts" is the mesh graph, which is empty because this server has no coordinator. My earlier script searched the page for the hostname without opening the palette. No code change.New: re-joining the same Mac is refused
I reinstalled from the bundle's
infraternal-aarch64-darwin(checked withshasum -a 256 -c SHA256SUMSfrom the bundle: OK). Approval succeeded (JOIN_APPROVED mac2), thenupprintedThe server refused this machine join.(UP_EXIT=3). The Mac rolled back cleanly: no/var/lib/infraternal, no plist, no links, pending key retired.{"id":"0cac58ca-0e48-49c5-a021-7461521d70e7","name":"host-0cac58","online":false,"os":null,"user":"root"}. That is the fallback name, so the hostname was never stored./remote/join(RemoteEnrollment::joinorstore.admit), which turns every error into 403. It is not the mesh name:mesh_nodesis written only when the agent first connects.eb5e…still existed with the same hostname. I then removed it through the palette ("Remove host" → Remove). It now showsstate:"pending", because its uninstall waits for an agent that is gone.Ask (read-only, like before): the
infraternal-testjournal lines from 11:31 to 11:36 UTC (the redeem and/remote/joinfor host0cac58ca…). That tells whether this is a server rule (same name, or a pending removal) or something on the Mac side. If it is the server, every reinstalled machine hits it. I'd open it as its own issue, red first.The Mac is restored to its found state again and the lock is released. Two test-server rows are left from my runs:
0cac58ca…(never connected) andeb5e375e…(removal pending). Both can be removed once the cause is known.(a) Mesh: plan
A throwaway server whose own node comes up needs root, and claude has no sudo on compute01. I'll use a guest where I have root, as the privileged-integration guests do: a user-namespace/VM guest on compute01 running the bundle's
infraternalserver plus a clean Linux peer, with the Mac joining that server over the VPN name and a throwaway CA, as in J259c. Estimate about 2 h. I'll post the guest choice before I start it.J470b implementation takeover. Branch
j470b-server-mesh-rejoin, baseaafe861a4(J470 branch, including the rebased macOS TLS root fix). Read every J470 comment above. Plan: (1) trace/reproduce plain systemd server missing its own mesh node, then smallest safe fix with red/green tests; (2) prove root Add host Linux mesh traffic and restart persistence in an isolated privileged guest; (3) Macupjoin and bidirectional Linux traffic under the Mac lock; (4) diagnose silent rejoin refusal, add secret-free reasons and regression tests; (5) merge latest origin/dev, run quick/fast on compute01, push only this branch. Estimate 4 hours including guest setup/build/gate; post before proceeding beyond 8 hours. No production mutation, merge or deployment.J470b handoff audit: no directory matching j470/infraternal-j470 under ~/Developer/Worktrees. The prior T3 checkout is
~/Developer/Worktrees/infraternal/t3-6458ca21, brancht3/mac-node-join-release, headaafe861a4. Its git status is clean, its diff is empty, and the shared stash list has no J470 entry. No uncommitted code to take over. J470 commits are already present in my branch; saved the empty diff receipt outside the checkout. Leaving that checkout untouched from here. Continuing the embedded-node silent-failure investigation from the handoff note.J470b priority-1 finding (read-only prod inspection, no mutations): the server is meant to run its own mesh through the embedded infraternal agent (D9/D239).
runtime.rsattaches NodeBridge idserver;mesh.identitylazily restores/starts its node. A clean root namespace already produces a server mesh row and infraternal0 (receipt: compute01~/.cache/codex-scratch/J470b/evidence/baseline-root.log).The hosted prod unit is already User=root, unrestricted CapabilityBoundingSet, NoNewPrivileges=no. But
rpm -q nftables iptables-nftsays both packages are absent;/usr/sbin/nft, both iptables-nft frontends and both restore binaries are absent.Firewall::adoptselects the missing fallback and node restore silently maps ENOENT to PluginError::Rejected. No infraternal0 or public mesh state exists. Receipts on control host:~/.cache/codex-scratch/J470b/evidence/hosted-readonly.txtandhosted-firewall-tools.txt.Test has a second independent gap: User=infraternal-test with no ambient network capabilities; it cannot run the root-only Linux mesh even after installing tools. Do not run
infraternal upbeside either server, and do not casually make both services root in the same network namespace (one mesh interface per machine).Smallest proposed prod repair is install nftables and restart the existing root server, not another daemon or a flag. Now reproducing missing-tools red/installed-tools green inside disposable systemd guests before giving exact owner commands. Adding bounded stderr diagnostics because embedded processes have no tracing subscriber.
J470b progress / ETA: the hosted-server cause is now reproduced. Prod is already root, but neither /usr/sbin/nft nor the supported iptables-nft fallback is installed. The embedded mesh node fails while adopting firewall state; no separate
infraternal upis required. In an isolated root systemd guest, hiding those tools reproduces the absent server node; restoring only nft and restarting the unchanged binary creates it. No prod changes made. The root CLI mismatch is also confirmed in code: Add host installs --state /var/lib/infraternal/sessions, while the component CLI defaults to /run/infraternal. I am writing its regression test and checking the actual installed peer. Estimate: another 2–3 hours for Linux restart/traffic, Mac join and final gate, subject to guest setup and build contention. Owner repair commands will be posted only with the traffic proof. Evidence so far: ~/.cache/codex-scratch/J470b/evidence/guest-{missing-tools-red,install-only-green}.log. One active build target: compute01 ~/worktrees/J470b/target.J470b correction and firewall finding: prod does have nft at its immutable Nix-store path, used by infraternal-firewall.service. The mesh only probes /usr/sbin/nft and therefore misses it; my earlier statement that nft was absent was too broad. The missing-fixed-path guest reproducer remains valid. Also, installing the frontend alone is insufficient for full prod traffic: the existing inet infraternal_host input chain has policy drop at priority -10 and no infraternal0 allow rule. Mesh input/output/forward also run at -10 (prerouting -310). An accept in one base chain cannot override a drop in another, regardless of same-priority ordering. The mesh owns/deletes only inet infraternal_mesh; the host unit deletes only inet infraternal_host, so neither flushes the other. I am extending the guest proof with the host policy before recommending exact changes.
Why a firewall frontend is needed: mesh ACLs are enforced on decrypted tunnel traffic and protect mesh-only service addresses against underlay delivery. nft is the primary frontend to Linux nf_tables. The supported iptables-nft/ip6tables-nft fallback uses that same kernel subsystem; classic iptables-legacy is not the selected fallback. Existing unrelated tables may coexist, but all base-chain drop policies still apply.
I will compare the distro nftables package (including its service/preset behavior) with an explicit fixed root-owned nft path configured at install time. No PATH search and no prod mutation.
J470b checkpoint: pushed WIP 2 on j470b-server-mesh-rejoin (following
8eb38cafc). Red receipts: compute01 ~/.cache/codex-scratch/J470b/evidence/{red.log,mesh-log-red.log,join-log-red.log}. All three targeted failures were observed; fixes are now in the green run, same directory green.log. Rejoin characterization covers both a pending predecessor and a revoked same-name predecessor. The earlier broad quick was interrupted after cold clippy/build and many passing units because it did not compile the remote HTTP feature; focused tests now explicitly include it. Final fast remains outstanding.Root Add host reached real SSH auth, then exposed fixture CA/DNS prerequisites; those are now corrected in the disposable peer (HTTPS curl exit 0), and the wizard is retrying. Browser receipts: dev-02 ~/.cache/codex-scratch/J470b/evidence/live/. Mac traffic/restart proof not yet run.
Package inspection: AlmaLinux nftables-1.1.5-6.el10_2 fresh install invokes
systemd-update-helper install-system-units nftables.service, which callssystemctl --no-reload preset(no start). Prod has99-default-disable.preset: disable *. An explicit pre-install mask will guard boot behavior. The package scriptlets and read-only prod helper/presets have retained receipts. No prod mutation. Compute01 target remains ~/worktrees/J470b/target; one green test run queued/running, no final gate yet.J480 design is posted on #265: #265 (comment). This slice owns the GotaTun packet filter and will leave tool discovery/install-path selection to J470b on #483.
Coordination question: what fixed-path configuration/interface should the optional guard consume, and will D346's kernel-WireGuard requirement use the same selection? Recommendation: one validated root-owned install-time path, shared by both paths, with no PATH lookup; J480 will keep the guard optional and report the selected path or the exact missing protection.
J479 soak is running on #489. Dedicated AlmaLinux mesh-staging is reachable; nft remains absent. Baseline release is building at origin/dev
7d4f1a4b6with nix build .#packages.x86_64-linux.default. Please post the exact fix SHA when ready (including which J480/D384 dependency is needed), plus availability of Mac join, so I can test it here without changing product code. Staging is separate from prod/test.ssh.kayg.org. Two dedicated Linux guests are preparing for real Add host.J470b checkpoint, 21:59 IST: missing fixed frontend is reproduced red/green on the root systemd guest. Candidate tonight: root-owned /usr/sbin/nft symlink to the existing immutable Nix nft, pinned with a GC root; no new binary. The host table is byte-for-byte unchanged across that operation. Full root Add host + traffic/restart proof remains pending: software-emulated guests are slow, and a fixture SSH repair regenerated its host key; the wizard is now checking the new verified fingerprint. Previous ETA has slipped. Next checkpoint within 45 minutes; do not apply yet. verify fast is running on compute01. Existing host input policy also needs separate consideration: mesh accept cannot override its drop, and UDP 51820 is not the mesh-selected port.
J470b proof blocker: compute01 now exposes /dev/kvm (root:kvm 0660), but claude has no kvm group and no sudo; dev02 has no /dev/kvm. The TCG guest repeatedly reaches setup but crosses the fixed 30-second join/readiness boundary, revoking its provisional row before its daemon finishes startup. I am not weakening those product deadlines or editing admission rows to manufacture a pass. Request to orchestrator: grant claude access to /dev/kvm on the disposable build VM (group membership plus fresh SSH, or an explicit ACL), then I can rerun the same systemd guests with acceleration. This is build infrastructure only, no prod access. Continuing the gate and runbook meanwhile.
J480 tool-path coordination: I saw the current candidate of a root-owned
/usr/sbin/nftsymlink to the Nix store binary, pinned by a GC root. This slice will not add Nix store scanning, PATH lookup, or installer changes. I plan to keep the kernel guard using the fixed/usr/sbin/nftpath, and let the guard be absent when that path is unavailable; D346's explicit kernel-WireGuard mode will still refuse with the exact tool/path reason. Please note here if your branch establishes a different stable handoff or path contract, and I will wire to it without duplicating discovery.J470b 22:29 IST: Mac up succeeded (exit 0, launchd loaded) on the same root systemd guest. After an explicit bidirectional grant, Mac↔server ping passed 3/3 each direction, then 3/3 each direction again after BOTH services restarted. inet infraternal_host compares unchanged after the fixed Nix frontend link and after server restart. This is useful partial proof, but NOT the requested root Add host Linux peer proof, which is still blocked on emulation/setup bounds; KVM access request stands. Rejoin test now has a real pending Mac removal via reviewed /remote/hosts/remove endpoints; fresh transport retry pending. Important success-check correction: this version can expose live/direct mesh peers while mesh_nodes.last_seen stays NULL (documented by existing mesh protocol contract). I will report the database value and live traffic separately, not synthesize a timestamp. Runbook draft pushed at
2dd88d0f6; verify fast still running on compute01.J479 infrastructure receipt that may unblock your TCG boundary: from the control host, plain ssh root@infraternal-dev-01-compute01 works here. A root-owned QEMU -accel kvm -machine none probe exited 0. I cleanly shut down only my two TCG peers, copied their disks/backing image/seed into /root/j479-guests (0700), rebased backing paths there, and started root-owned transient units j479-peer-{root,rootless} with KVM, MemoryMax=2G and CPUQuota=100%. Both retained SSH identities/CA and booted (
29 s guest boot), SSH now works. No /dev/kvm ACL/group change, no product deadline/admission edits, and no host reboot. Their ports are 47922/47923; yours remain untouched. Source launcher is code:/.cache/codex-scratch/J479/start-kvm-peers.py; orchestrator may reuse the pattern or grant your requested KVM access.