Mesh: NAT traversal and relay for mesh peers #220
Open
opened 2026-10-15 08:17:20 +00:00 by kayg
·
129 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#220
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 2026-10-15: build NAT traversal and the relay for mesh peers first (survey #219:
PathKind::Relayedis only a type today). D163 (relay fallback, back to direct), D131 (relay is one command and a "Run a relay here" box in the UI), D185, D197 M5 (paths), D265. Merges with or after mesh-autoswitch #201.Starting mesh-relay on branch mesh-relay at base/head
c87fc06314(origin/idle-cpu-199). Read #220 (no prior comments or linked parent), #219 body/comments and survey attachment; reading #201 history and existing shared interfaces before edits. Switch-over marker is absent: only single-crate checks/tests until it appears; no workspace/static build started. The work covers D131/D163/D196/D197/D198: discovery, peer ciphertext relay with TCP/TLS fallback, direct recovery, removable relay component, namespace proofs and perf-VM evidence. No deploy.Contract proposal before implementation (D163/D196/D197/D198): keep endpoint candidates separate from reachability and path selection. Discovery records must refer to the actual peer-traffic socket and a current network generation; bound candidates, reject unusable addresses, expire observations, and distinguish observed stable mappings from destination-dependent mappings without claiming a NAT filtering type from two samples. A mapping observed toward the control connection is not a WireGuard reflexive candidate. Add public kernel endpoint parsing for IPv4/IPv6 without exposing device private fields. First independent slice: pure discovery state and strict address validation in infraternal-mesh, with no listener, timer, new dependency, wire-format change or #201 selector change. Relay forwarding will remain ciphertext-only and use existing selected QUIC/TLS stacks; no alternative library/engine is being selected. Switch-over marker remains absent; workspace/static builds are still prohibited by this brief.
Starting research-nat on branch research-nat at base/head
e972f91dad(merge base with origin/dev is identical). Read #220 body/all 2 comments and #219 body/all comments; neither identifies a parent issue. Continuing the mesh-relay discovery contract rather than duplicating implementation. Deliverable: primary-sourced docs only, early findings here followed by research/nat-traversal/README.md; foundational engines remain owner choices. D196 stage-one both-port-changing relay boundary and D197 M5 hysteresis will remain explicit. No deploy; final task instruction authorizes branch pushes.Early research-nat design input for mesh-relay (#220); full primary-source document follows.
github.com/tailscale/tailscale@b83f28ebfb/wgengine/magicsock/magicsock.go (L1343-L1351).These are research recommendations, not changed decisions or implemented features. No foundational library selected; no runtime measurements performed.
Completed and pushed
1127aa19d: bounded peer-socket discovery model in crates/infraternal-mesh/src/discovery.rs. Six new tests cover mapping evidence, independent IPv4/IPv6, mapped IPv4 canonicalisation, generation retirement, exact expiry, monotonic clocks, the 16-observation cap and invalid addresses/lifetimes. Crate gate: 23 tests plus 1 compile-fail doc test passed; fmt and all-target clippy exited 0. No listener, wire change, runtime coordination or performance claim in this slice.fmt, clippy, crate tests.
Finding: Device::counters currently discards public WGPEER_A_ENDPOINT (field 4), while node_coordination constructs an endpoint from the control socket address + local WireGuard port. Those coordinates are not a discovered NAT mapping. Next slice decodes/report kernel public endpoints with strict IPv4/IPv6 sockaddr lengths, preserving multipart metadata and existing private-buffer zeroisation. A kernel endpoint may still be configured/unproven, so it must not be labelled authenticated reflexive evidence merely because it exists. Planning an isolated functional source-port-translation/rebinding proof before coordinator use. The switch-over marker is absent; no workspace/static build, perf claim, size delta or completion claim.
Step 1 committed/pushed:
0f54e86f64, docs-only design. 524 lines, 21 resolved source definitions; git diff --check and cargo fmt --all --check pass. No Rust/TypeScript code touched, so touched-crate tests/clippy and TypeScript checks are inapplicable. Final full gate remains pending.Design includes exact-socket/generation discovery, bounded 50ms checklist pacing and retry budgets (proposals), concurrent ciphertext relay and direct proof, D197 M5 hysteresis, explicit D196 hard-NAT stage-one boundary, PCP renewal, verified family/transport-specific auto-relay eligibility and finite resource limits. Handoff preserves reliable application offsets; arbitrary UDP losslessness is not claimed. Follow-up source comparison, measurement denominators, library candidates and namespace acceptance matrix are being added.
Finding: PCP renewal is randomized at 1/2–5/8 returned lifetime (RFC6887 §11.2.1), not a universally fixed half-life. NetBird config says 4s keepalive, but its pinned ICE fork task-loop inspection implies ~2s; this is source inference, not a measured runtime rate. A build-slot-wrapped baseline static build is queued for same-recipe binary comparison; no benchmark host or metadata endpoint used.
Completed and pushed
176aca846: contracts/mesh-discovery.md records socket/generation binding, monotonic expiry, bounded observations, address-family isolation, NAT-evidence limits and configured-vs-authenticated kernel endpoints. Keeps #201 selection and observer/route leases authoritative; no shared selector interface changed. Diff/format checks passed.Current uncommitted next slice: strict std-only Linux sockaddr decoder + optional public Counters.endpoint, the existing isolated mesh robustness harness extended with that exact decoder, and scripts/mesh-endpoint-proof.py using the existing namespace helpers plus a kernel_node rebind fixture command. Standalone decoder tests: 2 passed, including truncation and unknown family/size combinations. Disposable user/network namespace checks confirmed WireGuard creation, nftables and forwarding are available. These are functional capability checks, not traversal or performance acceptance.
Touched-plugin clippy/tests have been queued through build-slot since the earlier step; endpoint-clippy.log is still empty (no build started). After that gate: build the single-crate kernel_node fixture and run the real translated-port/rebinding proof. Full scripts/verify fast, static size comparison, relay runtime/TCP443, coordinator integration, the six NAT profiles and perf-VM measurements remain outstanding. The required switch-over marker is still absent; no workspace/static build started. #201 has been notified about the additive endpoint field.
Step 2: source comparison and owner candidate inventory committed on research-nat; current head is recorded below. Document now 769 lines /69 resolved source definitions. cargo fmt --all --check and git diff --check pass. No product/dependency changes. Full gate pending behind build-slot baseline comparison.
Findings now documented with pinned commits/functions/sections: Tailscale heartbeat3s/trust6.5s/idle45s are distinct timers; direct scoring includes non-RTT preferences and DERP home hysteresis. NetBird uses a pinned ICE fork, native ICE/relay race, then QUIC DATAGRAM/WSS race; not direct ICE-TCP. Studies retain 2005 router-report, 2022 volunteer, December2022 DCUtR and May2026 ProbeLab cohorts separately. Proposed native checker is not an ICE/RFC7675 compliance claim. Engine choices stay open; no comparable native ICE/relay RSS or throughput was found, and unknown cost is explicit.
3d98fc4801Relay integration findings before transport wiring: production host leaves are clientAuth-only (crates/infraternal-server/src/ca.rs:239), while existing QUIC/TLS listeners correctly require server authentication/name verification (crates/plugins/remote/src/remote_quic.rs server_config/client_config; remote_tls.rs server_config/client_config). A standalone node relay therefore needs a properly issued server-purpose identity through the existing CA/role enrollment; reusing a host client leaf as a server or relaxing certificate purpose/name checks would be incorrect. The server already has its separate server-purpose leaf (ca_bootstrap.rs:239). No verification bypass or new crypto engine is being introduced.
The TCP/TLS 443 listener also has to respect an existing HTTPS binding: server HTTPS bind and remote transport listeners are separate owners today (runtime.rs:305,420). Relay registration should remain a generic removable plugin capability, rather than a relay-specific core listener. These are implementation requirements for the remaining runtime, not evidence that peer relay exists. Full static growth cannot be measured while the switch-over marker forbids workspace builds; it remains unmeasured, with the >16 KB justification and >64 KB owner limits unchanged.
Steps 3/4 committed and pushed:
baa9e6c989(matrix/acceptance) then13b800213d(review clarifications). Full design at head: 1,054 lines, 71 sources, 81 ordered mapping/filtering combinations plus special cases, explicit proposed budgets, scale arithmetic, traps, ten implementation rules and owner questions.Findings incorporated: D166 protects reconnect/new permitted channels among already-known members during coordinator outage; directional inner initiation still needs bidirectional encrypted replies; a proved direct path can win before relay. Hard quota exhaustion explicitly pauses/errors with retained offsets if no alternative exists. Four separated measurement segments are not called a continuous soak. All source/statistical/numerical claims separate inspected constants, published evidence and proposals.
Validation: cargo fmt --all --check, git diff --check and document checks pass; 43 GitHub blob references/28 pinned files fetched with valid line bounds, zero errors. Source checks. Author review follows mesh transport/data-plane checklist; independent source/eligibility review corrections applied. Full final gate and same-recipe static size comparison remain pending behind the host build-slot queue. No runtime measurements, new libraries, code or UI changes.
INCOMPLETE checkpoint at pushed head
176aca8461. Committed:1127aa19ddiscovery model;176aca846discovery contract. Their crate gate passed 23 tests + 1 compile-fail doc test, fmt and all-target clippy (gate log).Uncommitted endpoint slice is preserved in the worktree and this complete patch, SHA256 8c838d59a776099c003bbee8fbea2093dc8f10b5795e007779736f0f6c589259. Standalone production decoder tests passed 2/2; root and isolated-harness fmt checks passed; NAT rule installation/replacement passed in an owned disposable namespace. Full translated-port/rebinding proof is written but NOT RUN.
Blocked validation: all four build slots stayed occupied. The queued touched-plugin clippy never started; stopped only our queue wrapper 699977 and its wait child 977910. Python 699971 also exited; ps showed all three gone. No daemon/server or namespace process remains. Own target directory removed; evidence retained. Scheduling/cleanup receipt. Orchestrator/build capacity is needed for the next gate; the switch-over orchestrator must publish switchover-merged before rebase/full builds.
Resume: run slot-wrapped mesh-plugin all-target clippy and library tests with --no-default-features --features agent; build its kernel_node example and run unshare -Urn python3 scripts/mesh-endpoint-proof.py --fixture . Check/run the existing isolated crates/infraternal-mesh/fuzz harness; its new path module compiles the exact std-only production decoder. Split parser/counter, harness and proof commits after their gates; notify #201 of the committed optional Counters.endpoint field.
Still required: actual coordinator discovery/simultaneous open; removable relay runtime and proper relay identities; TCP/TLS443; sustained direct recovery with #201; all six NAT profiles; perf-VM measurements at scale; merge-base stripped-static size delta; final scripts/verify fast. The required switch-over marker remains absent. scripts/verify fast was NOT RUN (prohibited by brief), so there is no final fast output to quote and no completion/size/performance acceptance claim. Do not close this issue or treat this checkpoint as the complete #220 implementation.
Verification scheduling blocker at 2026-10-15 08:58 UTC: static comparison has waited since 08:28; all four host build slots are occupied by active wrappers (size-ratchet-r2-review since08:13, idle-cpu-199 since08:27, terminal-links since08:01, unnamed fast gate since08:00). Research, review and all four documentation commits are complete/pushed at13b800213d5e430470bf02941db0b29f6f42f058. Full design is available to mesh-relay now.
Stopped only this job's queued wrapper714456/sleep1074281 (no build child had started) and queued one combined reservation: merge-base-equivalent static recipe, then branch-head scripts/verify fast, then byte/hash comparison. Only README differs from merge base; build scripts do not stamp Git identity. This avoids a second reservation wait. Need a normal free build slot; continuing to wait, no scheduling limits bypassed and no other process changed. No benchmark or remote host used.
Step 5 committed/pushed
bdb4e372b2: incorporated mesh-relay's identity/listener findings after verifying ca.rs:239 (node client-auth only), ca_bootstrap.rs:239 (server-purpose leaf), and runtime.rs:305/420 (separate HTTPS/remote listener owners) at our baseline. Proper relay-purpose issuance/name/renewal and generic TLS443 listener sharing are explicit prerequisites; no certificate-purpose/name bypass or relay-specific core wiring. Added wrong-purpose/name/expiry and occupied443 acceptance cases. No code/contract/decision change.Current full design:1,081 lines/75 resolved sources. cargo fmt --all --check, git diff --check and document invariants pass. Final static comparison + scripts/verify fast reservation still waiting; it will read/run this head. This update also acknowledges mesh-relay's incomplete checkpoint; no relay runtime completion is implied.
2026-10-15 09:14 UTC scheduling update: original static request has waited about45min; combined baseline+final-gate reservation remains queued. Slot1 changed from size-ratchet review to another fast gate at09:04; slot4 changed to size-ratchet-r2-review-retry at09:07. Slots2/3 still show idle-cpu-199/terminal-links. No research-nat compile/test has started; both build transcript files remain empty. The normal wrapper polls slots every20s without a FIFO queue, so slot turnover does not imply this waiter acquires one. No scheduling code/limit/other process was changed. Research and source review are pushed atbdb4e372b2ecf6a89cea6da61c76ffb1677a69f1; continuing to wait for required final validation.
2026-10-15 09:28 UTC: original verification request has waited about1h; combined comparison+final-gate reservation still has not acquired a slot (empty verification-session.log and baseline-static-build.log). Current pushed research head
bdb4e372b2. Document/source/author checks pass; full fast gate and static size remain unrun/unmeasured. Continuing normal build-slot wait; no capacity limit bypass or other process changes. Resume command/provenance script is /home/kayg/.cache/codex-scratch/research-nat/verify-sequence.sh under the same wrapper, CARGO_TARGET_DIR research-nat/target and TRACER_TEST_PORT4320. No product code changes or runtime acceptance claims.Step6 committed/pushed41531643151c5fd1320016b222d717ebf28e4bef: clarified D278's invisible normal flow without deleting D196's per-node mapping override; no NAT-type choice/additional traversal step is introduced. Local/reflexive equality explicitly compares IP:port, not just IP. No decision or code change. Document checks pass at1,083lines/75source definitions; fmt and diff checks pass. Full design. Combined final verification remains queued and will use this head; no gate/size result yet.
Verification reservation acquired; comparison build is now compiling. Script confirms head41531643151c5fd1320016b222d717ebf28e4bef, merge basee972f91dad67ac1f1274a4e4cd70df24ee052bc1 and only research/nat-traversal/README.md differs. Document invariants pass:1,083lines/75sources, all labels/local links/role/license/wording/ten rules/scope/arithmetic checked. Same reservation runs the baseline-equivalent static recipe, final branch-head scripts/verify fast, and binary byte/hash comparison sequentially. These are build/functional checks, not NAT performance measurements. Original request08:28UTC; actual slot time is in wrapper provenance. No code/dependency/decision changes.
Static comparison build completed at head
4153164315. Merge-base compiler inputs are identical (only the research Markdown differs; no Git identity stamping). Exact shipping recipe produces a stripped static x86_64 binary of 22,277,392 bytes; SHA256 fce0032db0b4f651b2c5055ab649182e53cbd6ad1bcec8f31f0dfdba626bcf77. Configured ceiling in scripts/verify.d/rust.sh is 22,204,304 bytes, so unchanged inputs already exceed it by 73,088 bytes. This is not a measured branch delta yet; the final-head rebuild/comparison follows.scripts/verify fast is now running in the same build-slot reservation. The research job will not change the ceiling or product code to conceal the baseline mismatch. Full gate output and final byte/hash comparison will be attached after completion. No NAT runtime/performance measurement or new dependency is involved.
First fast attempt found a gate-launch configuration error, not a product assertion failure. The agent_daemon integration test stopped before starting its fixture because I set INFRATERNAL_TEST_STATE_ROOT to the 50-byte nested research-nat/state path. Verbatim failure:
scripts/agent_fixture.py:41–56 explicitly enforces <108 bytes including the longer upstream socket. Corrected setting is the 44-byte task scratch root /home/kayg/.cache/codex-scratch/research-nat itself; helper-only checks for tb/ts/vc/mw/r99 generate 105–106-byte paths and pass without launching a daemon. The original full gate is finishing static checks; after it exits I will rerun scripts/verify fast with this setting. No code, test threshold, host fixture or size ceiling changed. Head remains
4153164315. Both attempts will be preserved and the final result reported explicitly.First gate attempt finished at head
4153164315. Stripped x86_64 comparison: 22,277,392 bytes on both observations, delta 0 bytes, identical SHA256 fce0032db0b4f651b2c5055ab649182e53cbd6ad1bcec8f31f0dfdba626bcf77. Baseline compiler-input receipt; head receipt. Only the research Markdown differs from the merge base.Initial test row failed before fixture startup because of my too-long state root (111-byte socket path). The corrected root passes the harness guard at 105–106 bytes; a full corrected gate is queued. No source or test threshold was changed. Static checks also confirm unchanged absolute ceiling mismatches: x86_64 22,277,392 vs22,204,304 (+73,088); aarch64 19,705,112 vs19,638,744 (+66,368). No ceiling was raised. These are artifact sizes, not NAT performance measurements.
Initial Rust summary, verbatim (not the final corrected result):
Complete initial gate transcript. The first transcript and receipts are preserved. Corrected gate is pending normal build capacity; validation is not claimed complete.
Verification status: corrected final
scripts/verify fasthas been queued behind the host build-slot wrapper since 10:38:55 UTC (about 30 min). Head remains41531643151c5fd1320016b222d717ebf28e4bef, pushed, docs-only. No second slot command started. Initial full-gate transcript and zero-byte x86_64 comparison are attached above; corrected fixture root is checked and waiting. No product change or ceiling change is proposed.Corrected final gate remains queued about 45 min since 10:38:55 UTC. No gate process has started for this retry; head
41531643151c5fd1320016b222d717ebf28e4befremains pushed and unchanged. Current regular-slot owners at 2026-10-15T11:23:42.119842+00:00:Continuing to wait through the required wrapper. Research handoff, first gate receipt, corrected fixture-root explanation and zero-byte size comparison are already above. No other job was interrupted.
Corrected final gate has waited about one hour since 10:38:55 UTC and has not acquired a build slot. Research remains pushed at
41531643151c5fd1320016b222d717ebf28e4bef; no further changes needed. Current regular-slot records at 2026-10-15T11:38:56.191446+00:00:Continuing to wait through the wrapper; no other processes touched and no parallel build from this job.
Corrected final
scripts/verify fastacquired a build slot at about 11:58 UTC after roughly 79 min queued. Head41531643151c5fd1320016b222d717ebf28e4bef; validated scratch fixture root/home/kayg/.cache/codex-scratch/research-nat. Document checks passed (1,083 lines; 75/75 reference labels). CLI unit tests: 76 passed, 1 ignored; now running daemon integration with the corrected path. No code or test threshold changes.Corrected state-root full gate completed at head
41531643151c5fd1320016b222d717ebf28e4bef, exit 1. The daemon integration andvt_creditpassed. Rust tests then stopped atdaemon::gone_tests::a_vanished_session_gets_a_final_close_and_the_channel_stays_up:daemon_gone_tests.rs:111reportspath must be shorter than SUN_LEN. This separate Rust fixture uses TMPDIR plus 25-character random directory plus sessions/32-hex.sock: 121 bytes under the normal job/tmp path, versus Linux maximum 107. A temporary short alias/home/kayg/.cache/codex-scratch/rnpoints only to this job's tmp directory and yields 107 bytes; it will be removed during cleanup. No test or product code changed. Single exact-test confirmation is running before the remaining workspace gate. Static size remains 22,277,392 bytes x86_64, SHA unchanged, delta 0; ceilings still reject x86_64 (+73,088) and ARM (+66,368).Gate summary verbatim:
Exact existing workspace test binary rerun with short TMPDIR alias:
daemon::gone_tests::a_vanished_session_gets_a_final_close_and_the_channel_stays_upPASS (1 passed, 110 filtered). Alias points into this job's scratch only. No repository/test changes. The separate Cargo invocation began a different feature-graph rebuild, so it was stopped by its verified PID 2620725;ps -p 2620725and own scratch build-child scan show no remaining process. Full final gate is queued again through build-slot from about 12:05 UTC, same head41531643151c5fd1320016b222d717ebf28e4bef, with both fixture roots bounded.second-verify-fast-tmpdir-path.log
short-tmpdir-existing-test.log
Final attempt with both fixture roots bounded stopped before Rust checks: latency-proxy self-check missed its existing timing tolerance on the shared build host. Diagnostic only, not NAT performance acceptance:
latency proxy: configured 300 ms RTT did not arrive: p50=312.84 ms p99=366.10 ms. Head unchanged41531643151c5fd1320016b222d717ebf28e4bef; exact previously failing socket test already passed. Preserving the transcript and queueing unchanged full gate again, with at most three immediate retries only when this preflight fails before Rust checks. No threshold, product code or size ceiling changed; no load generator started. All other failures stop the run for inspection.Documentation delivery / mesh-relay handoff: six atomic commits pushed on
research-nat; remote head verified as41531643151c5fd1320016b222d717ebf28e4bef. Onlyresearch/nat-traversal/README.mddiffers from basee972f91dad67ac1f1274a4e4cd70df24ee052bc1(1,083 lines, 75 used source labels).Full design at the pushed head.
Handoff priorities: exact real-data-socket/generation discovery; concurrent direct checks and authenticated ciphertext relay; D196 both-hard relay boundary; shared D197 M5 selection; sparse bounded state at D170 scale; outside-proved Linux relay eligibility, including cloud 1:1 translation; correct relay server-purpose identity plus generic listener sharing; D166 offline known-member continuity; real MTU/IPv6/roaming proof; D278 invisible ordinary setup. Timers, relay quotas and namespace budgets remain explicitly unmeasured proposals. No engine/library/data choice made. Full comparison, 22 traps, ten rules, source sections and owner questions are in the document.
Final
scripts/verify faston head: exit 1, red. Workspace test summaries: 1,013 passed, 0 failed, 138 ignored across 107 summaries. Both socket-fixture path issues were resolved through scratch environment paths; the existing tests then passed without code changes. The final timing preflight passed unchanged. TypeScript stage was not reached after Rust static failure; no TypeScript changed.Final gate summary, verbatim:
Static gate size lines, verbatim:
D243 same-recipe x86_64 comparison: baseline compiler inputs equal merge base (the sole difference is Markdown; build scripts do not stamp Git identity). Baseline-input and final artifacts are both 22,277,392 bytes, delta 0 bytes, identical SHA-256
fce0032db0b4f651b2c5055ab649182e53cbd6ad1bcec8f31f0dfdba626bcf77. This is an identical-compiler-input comparison in the same checkout/path, not a separate detached baseline build. Existing ceilings reject those unchanged inputs: x86_64 +73,088 bytes; ARM +66,368 bytes. No ceiling or product-code change made by this research job. ARM key-memory runtime proof was explicitly skipped on the x86_64 host.Review/validation: mesh transport and data-plane author checklists reviewed; independent source reviews and corrections incorporated. Document roles/license/links/wording/ten rules/scope/arithmetic validated; pinned-source check verified 43 GitHub references across 28 files with zero invalid line bounds. No NAT runtime, namespace or performance acceptance is claimed by this documentation job.
Cleanup: worktree clean; remote and local head match; no remaining own build/test processes; own target, temporary source clone/state and short scratch alias removed. Evidence retained outside the checkout. Issue remains open. The unchanged static ceilings remain a verification blocker for the orchestrator; the research is available to mesh-relay now.
Final receipts:
final-verify-fast.log
final-static-size.json
author-review-final.txt
final-document-validation.log
cleanup.json
third-verify-fast-preflight.log
Design proposal: direct-path NAT traversal + relay fallback (orchestrator synthesis, 2026-10-21)
Read-only design pass. No code, no decision changed, no library chosen. This builds on what is already on #220: the
research-natdesign at41531643(research/nat-traversal/README.md, 1,083 lines) and themesh-relaycheckpoint at176aca846(crates/infraternal-mesh/src/discovery.rs,contracts/mesh-discovery.md). This comment does not repeat that document. It turns it into one concrete design for the kernel-WireGuard data plane we already ship, adds the India↔Germany arithmetic, and lists the questions that still need the owner.Already decided, not reopened: D163 (kernel WireGuard on Linux; the server always runs a relay; direct first, back to direct when it opens), D131/D132/D197 (any node can relay; add-host turns relay on; one command; "Run a relay here" box), D176 (quinn + rustls + ring), D185 (relay with the lowest measured RTT, switch mid-session without losing bytes), D196 (our own discovery + simultaneous open; hard+hard relays in stage 1; PCP and NAT-PMP first, then UPnP, on by default with a per-node switch; TCP/TLS 443), D197 M5 (switch only on a clear, sustained win; direct wins a tie), D278 (no NAT controls; the path shows only in add-host/loading steps and the Mesh label), "latency is the design case": the server stays on monitor01.
0. Where the code is today (dev
aba6d85ce, branchmesh-relay176aca846)crates/infraternal-mesh/src/backend.rs:10PathKind { Unknown, Direct, Relayed { relay } }Relayedcrates/plugins/mesh/src/node_coordination.rs+dataplane.rs:38Peer.endpoint: Option<SocketAddr>crates/plugins/mesh/src/wireguard.rs:661Device::endpoint()WGPEER_A_ENDPOINTand keeps the handshake state. This is the seam for moving relay→direct.wireguard.rscountersWGPEER_A_ENDPOINT; the uncommitted patch on #220 fixes thisdiscovery.rs(mesh-relay)crates/plugins/relay/src/{lib,cli}.rsrelay up/downare declarednot_available_yetcrates/infraternal-agent/src/relay.rsdataplane.rs:70ServerPath { mesh, enrolled_fallback }+ #201Two blockers come from the code, not from network theory:
1. Candidate discovery on the real socket
Proposal (hybrid "same 5-tuple side channel"): the kernel keeps the WG fast path. Discovery and connectivity checks run as side-channel packets that use the same source IP:port as the WG socket:
AF_INET/AF_INET6,SOCK_RAW,IPPROTO_UDP, we run as root per D163) writes UDP datagrams with source port = WG listen port. To the NAT they are the same flow, so they create and refresh the same mapping.AF_PACKETsocket with a classic-BPF filter:udp dport == wg_port, and the payload starts with our probe magic and does not start with a WireGuard message type byte (1–4 followed by three zero bytes). The kernel WG module also sees the probe and drops it as invalid. That is a few hundred nanoseconds for each probe packet; data packets never reach userspace.discovery.rsbook stores it. Two observers on different IPs giveMappingEvidence::Same/Different(endpoint-independent vs dependent mapping), as the contract already says.Network change (netlink
RTM_NEWADDR/DELADDR/NEWROUTE, resume after suspend): bump the discovery generation (Discovery::restart), withdraw old candidates, re-probe at once. Relay stays usable the whole time.2. Hole punching (simultaneous open)
With seam B, kernel WireGuard does the punching itself. A WG handshake initiation is a UDP packet from the right 5-tuple.
punch_attime. Both sides receive it inside one control round trip.Device::endpoint()set the WG endpoint to the proven address. A shortpersistent_keepalive(proposal: 20 s, only while the pair is active) holds the mapping. The current code has a fixed keepalive (#219).Expected outcome by NAT pair (RFC 4787 terms, mapping/filtering; success rates must be measured, D196):
2a. Hard NAT and the birthday technique (later, only on evidence)
For one EDM side and one stable side: the hard side opens
mmappings and the easy side trieskrandom ports out of a pool ofN. P ≈ 1 − (1 − m/N)^k. With m=256, N=64,512 and k=1,024 this gives about 98% in the model. Real port pools, port-preserving allocators and conntrack limits change it. research-nat §3 explains why it is not measured.Constraint from our code: the hard side's
msockets must be the WG socket for the winner to carry data. With kernel WG there is one listen port, so the birthday technique requires seam A (the userspace shim) for that pair, or a per-pair second WG interface. So it costs exactly what seam C already pays for relayed pairs: it fits C, and it does not fit a pure B.Port prediction (sequential allocators): cheaper. One side probes
last_seen_port + 1..+16from the single WG port. It is worth measuring first because it needs no extra sockets.Trigger for building either one (D196 says "only if measurements show many relayed pairs that could have been direct"): see Q4.
3. Relay: data path, selection, failover
Data path: the relay forwards WireGuard ciphertext only. It never has a mesh key (research-nat §4).
runtime.rs:305/420). TCP-over-TCP meltdown does not apply: the inner flow is WG/UDP. Inner TCP still sees head-of-line stalls on loss, which is accepted for this fallback.ca.rs:239). A node relay needs a server-purpose leaf issued through role enrollment (D131 one-time token). We never relax the name or purpose checks.Selection (D185 + D197 M5): the score is the end-to-end RTT through the relay,
RTT(A,R) + RTT(R,B), measured with authenticated probes through the relay, never A's leg alone. Rank by median and p95 of 5 samples over ≥3 s. A relay must be eligible first (healthy, capacity left, MTU ok). Quality only orders the eligible ones. Each node keeps one home relay connection for rendezvous and for being reached. It opens a data relay per pair only when that relay wins by the M5 margin.Failover:
Terminal/VNC/backup streams already keep their own byte offsets (D128,
infraternal-agent/src/relay.rs). A route change below them loses no application bytes. Arbitrary inner UDP can drop packets during a switch. That is stated, not hidden.4. Port mapping (PCP / NAT-PMP / UPnP-IGD)
D196 has already decided the order and the default. Design points that follow from our code:
wg_port, UDP only, only the node's own address. The mapped external IP:port becomes aport-mappedcandidate. It is proven like any other candidate (a lease is not reachability; a CGNAT can sit in front).5. Relay → direct upgrade without breaking streams
WireGuard sessions are independent of the endpoint.
Device::endpoint()already changes the endpoint and keeps handshake state, so the inner keys, counters and every inner TCP connection survive the move. The design has to handle the roaming hazard:Device::endpoint(B, direct). The next packet from A arrives at B on the direct path. B's kernel roams to it automatically, so B needs no control message and no extra round trip. B's agent sees the endpoint change in counters and confirms.What the user sees (D278): add-host shows "Connected (relayed)" → "Direct". After that, only the Mesh label changes. The switch never shows a notice.
6. The latency case: server in Germany, clients in India
Measured legs (research/data-path/REPORT.md, 300-sample runs): monitor01 (Essen) ↔ Mac VM (India) 198.1 ms mean; build box (Falkenstein) ↔ Mac 152.7 ms p50 TCP connect; Mac ↔ o1 (Hyderabad estimate) 23.4 ms (5 pings); monitor01 ↔ o1 171.1 ms (5 pings).
What follows:
7. Library and engine candidates (not chosen; owner decides on measurements)
Taken from the inventory in research-nat §10, cut down to what this design needs:
discovery.rsbook, (b)str0m's ICE agent (already in the binary for the browser direct path, D192), (c)rtc-iceportmappercrate, (c)igd-next+ our own PCP/NAT-PMPEach needs
cargo tree -e features, a stripped static delta on x86_64 and aarch64, RSS at 1/100/1,000 active peers, and setup p50/p99 on the perf VM with the India↔Germany latency proxy.8. Gates for this slice (beyond
scripts/verify fast)Namespace matrix (
scripts/mesh-endpoint-proof.py, already written, not yet run): EIM/EIF, EIM/APDF, EDM on each side; UDP denied (TLS 443 only); stale endpoint after a rebind; suspend and roam; PTB dropped at low MTU; relay killed mid-stream; the flip-back test from §5.4; DE↔IN latency proxy (198 ms). Numbers per run: setup p50/p99, time to direct, the number of bytes in a numbered stream that are lost (must be zero for reliable streams), CPU and RSS per pair.Open questions for the owner
Q1. How does discovery see the kernel WireGuard socket? (blocks everything; §1)
Next step: a perf-VM job measures A against B (µs per packet, CPU per Gbit/s), then you choose.
Q2. Where does rendezvous signalling go?
Q3. Which relays may a pair use?
Q4. When does hard-NAT prediction get built? (D196 says "if measurements show many relayed pairs that could have been direct")
Q5. Numbers for the switch rule (D197 M5 "clear, sustained win")
Q6. TLS 443 fallback scope in the first release
Q7. Relay framing over QUIC
Q8. Port-mapping and ICE checklist implementation (§7): owner choice after the size/RSS/idle comparison job. No recommendation until it is measured. What I lean towards: our own checker over
discovery.rs(the smallest feature graph), withstr0m's ICE agent measured as the alternative because it is already in the binary.Proposed order once Q1 is answered: real-socket observation (seam) → relay component with QUIC DATAGRAM + TLS 443 → rendezvous + simultaneous open → upgrade/flip-back handling on the #201 selector → PCP/NAT-PMP/UPnP → node-relay qualification and identities → hard-NAT experiments only if Q4 triggers.
Nothing above is measured unless it cites research/data-path or the #220 receipts. The µs/CPU figures for seam A are estimates that the Q1 job must replace.
kayg referenced this issue2026-10-28 17:53:55 +00:00
kayg referenced this issue2026-10-28 17:53:56 +00:00
Grill round 1 answered by the owner (2026-10-30), recorded as D313 on dev (
db8c1bdf6).Q1 seam C · Q2 home relay with pre-signed tickets (after coordinator-only works) · Q3 any relay in the mesh · Q4 counters-gated, sequential prediction first · Q5 max(5 ms, 15%) over 5 samples / ≥3 s, 10 s dwell, direct wins ties · Q6 TLS+ALPN on 443 plus WebSocket · Q7 QUIC DATAGRAM.
Open: Q8 (port mapping + connection checklist implementation) waits on the comparison job. Next: one measurement job builds a perf kit for (a) seam A vs B, µs per packet and CPU per Gbit/s, and (b) the Q8 candidates' stripped static size delta (x86_64/aarch64),
cargo tree -e features, idle tasks/timers and RSS at 1/100/1,000 peers. The orchestrator runs it on a perf-test VM.Starting measurement-only research on mesh-measure-220, base
db8c1bdf6(origin/dev). Read #220 body/all 30 comments, shared rules, repository contracts and D313. Preparing standalone seam perf relay kit and throwaway Q8 size probes; no shipped dependency or product changes. Build-host smoke will be labelled separately from perf evidence.Measurement checkpoint: branch mesh-measure-220, base
db8c1bdf6. Pinned stripped-static baselines built sequentially: x86_64 21,437,872 B; aarch64 19,601,832 B. No product inputs changed. Q8 feature builds are running in a disposable origin/dev export; lock resolution retained every baseline (name, version, source) tuple.Finding: neither str0m nor its extracted ICE crate is appears in origin/dev Cargo.lock at this base. Measuring is 0.11.1 as a new dependency; the design's zero-increment premise does not apply here. A caller-owned raw/packet socket ICE probe is being added.
Parser finding (source inspection, not a runtime security verdict): portmapper 0.20.0 depends on igd-next 0.17, whose common/parsing.rs entry points call xmltree::Element::parse directly. The separate igd-next 0.18 candidate adds MAX_RESPONSE_BYTES and MAX_XML_DEPTH=64 checks. Feature trees, full license inventory and per-target deltas will be attached with final results.
Kernel-only functional namespace smoke passed inside a disposable privileged Debian 13 container on the aarch64 build host; before/after links, netns, module names, qdiscs and net.* sysctls diff was empty. Build host, not perf evidence. Host sudo requires a password, so the perf VM root/systemd wrapper remains for orchestrator execution.
Step 1 committed/pushed:
0e3fc2b85, standalone Q1 kit and real-port ICE probe. Functional smoke passed baseline/A/B, 1-pair shim allocation and both raw ICE endpoints (12 sent/12 accepted at each endpoint); all five before/after configuration diffs were empty. Build host, not perf evidence. Fixed an early probe exit that stopped replying before the other endpoint nominated; the passing probe keeps servicing packets through its bounded proof window. No product source/dependency changes. Q8 final size sweep is running sequentially; final full gate remains pending.build-host-smoke.log
baseline-artifacts.json
Q8 measurement checkpoint at
8bb97ab88c8de18e40b7c65dfb5a9392a901cc21(basedb8c1bdf6242). All 14 stripped static candidate/harness builds succeeded with the pinned recipe, real reachable call paths, and all 393 original package tuples retained. No shipped manifest, lockfile or production source changed. Baseline x86_64 = 21,437,872 bytes; aarch64 = 19,601,832. Shared harness overhead = +896 / +4,864 bytes.The own rows are incomplete prototype estimates; igd also includes our prototype PCP/NAT-PMP path. These are minimum reachable-path deltas, not complete feature budgets. Neither str0m nor
isexists in the recorded dev lockfile, so its zero-increment premise does not hold here.Functional raw-socket proof: both
is0.11.1 endpoints connected while the real kernel WG device owned UDP/20000 (sent=12 accepted=12each). The final short namespace pass succeeded for baseline/A/B/RSS/raw ICE, with five empty before/after state diffs: build host, not perf evidence. Constructor observations: own variants 0 tasks/timers; portmapper 1 service task and no lease timer without mapping; igd 0; is 0 tasks, initial deadline None, after first tick Some(3s); rtc 0 tasks and empty-constructor deadline None. Features/parser/licence inventory and final gate follow in the finish comment.mesh-measure-220.tar.gz
build-host-smoke-final.log
Kit SHA256:
0d3b861df6aff85d18b47d5ac9285c5f0fc19b21a04e391c633985e5222eec6c(1,341,333 bytes). Orchestrator: extract into~/infraternal-perf/mesh-measure-220, then run:Each case takes its own perf lock inside the root perf.slice scope; do not add an outer perf-run lock. The full perf run is pending orchestrator relay. No build-host timing is offered as performance evidence.
The first full gate exposed my scratch-path setup error: its short TMPDIR/test-state path was a symlink, deliberately refused by replacement admission. I will rerun with a real private directory. The separate
.sha256upload was rejected with HTTP 422; the digest is recorded above.Measurement handoff, head
8bb97ab88c8de18e40b7c65dfb5a9392a901cc21, pushed tomesh-measure-220only. Basedb8c1bdf6242; no foundational choice or production dependency change.Q8 stripped static bytes (x86_64 / aarch64), pinned static recipe and original baseline package versions. Baselines: 21,437,872 / 19,601,832 bytes. Harness-only deltas: +896 / +4,864 bytes.
All inventoried licence options are compatible with AGPL; no candidate dependency is shipped by this branch. The new MPL-2.0 attohttpc source does not carry an additional incompatible-with-secondary-licences notice. Full inventories and feature trees are attached for review; this is not a new foundational choice.
Unused task counts/deadlines are functional observations plus source inspection, not measured wakeup/CPU acceptance. Minimal active paths exited successfully in an isolated container; portmapper reported no gateway, so no successful real-router mapping is claimed.
The
isrow measures the exact extracted ICE core used by str0m 0.24.1, not the unrelated media stack. Neither package was present in the recorded dev baseline. The committed raw-socket probe connected both endpoints next to the real kernel WG port (sent=12 accepted=12each); it is a socket/API proof, not a production authenticated demultiplexer or NAT/conntrack proof.Q1 kit: static workers for both architectures, sources, run script, README and notices; 72 data cases (baseline/A/B-filter-only/B-with-probes × 3 payload sizes × 1/8 flows × 3 reps), RSS 1/100/1,000 active pairs, and raw ICE proof. Each case independently locks through perf-lock inside a root systemd perf.slice scope, with timeout/cleanup and before/after host configuration diff. The five-case container smoke passed with every diff empty: build host, not perf evidence. The real perf VM numbers and root systemd wrapper validation await orchestrator relay; no build-host timing/RSS is offered as performance evidence. Broader #220 §8 product NAT/roaming/flip-back/TLS443/MTU/handoff gates are not claimed.
Kit SHA256
0d3b861df6aff85d18b47d5ac9285c5f0fc19b21a04e391c633985e5222eec6c, 1,341,333 bytes. Exact orchestrator command after extracting the attached archive:Do not add an outer perf-run lock: every case locks separately. The expected measurement run is below 20 minutes (30-minute budget, excluding queue time).
Production x86_64 and aarch64 artifacts are SHA256-identical to baseline: 0 bytes shipped growth on both targets. Final
scripts/verify fastgate details and cleanup receipts follow below. Initial test-environment errors (symlink ancestor, then a 108-byte Unix socket path) are preserved in the logs; the final run uses real private directories and a 107-byte socket path. No production code or size cap was adjusted.Final gate is red, not waived:
scripts/verify fastexited 1. Nine Rust rows pass;testfails in the unchanged SSH streaming fixture andstatic buildsfails the unchanged baseline budgets. The TypeScript phase is not reached after the Rust failure. Exact test recheck on this branch and an untouchedgit archive db8c1bdf6242source export both fail identically; their logs are in the evidence bundle. No unrelated product fix or budget ratchet is included.Verbatim final Rust gate output:
Verbatim relevant diagnostics:
The research-specific cleanup regression suite passes all four cases, static C/ICE builds and the isolated Q8 probe checks pass, and both raw ICE endpoints connect. The full gate transcript remains attached, including the failure; this is a measurement handoff, not a green product acceptance claim.
Cleanup completed: CARGO_TARGET_DIR, candidate binaries, both disposable source exports, relay staging, job-local tools, temporary roots and the unique smoke image deleted. No process is executing from the job artifacts. Branch worktree clean; remote head matches
8bb97ab88c8de18e40b7c65dfb5a9392a901cc21. Final kit retained for relay. Issue left open.Attachments (83-file evidence bundle includes resolved Q8 Cargo.lock, complete feature trees, licences, all artifact/hash receipts, build logs, active/idle functional probes, review, baseline failure and cleanup receipts):
mesh-measure-220.tar.gz
build-host-smoke-final.log
q8-evidence.tar.gz
verify-fast.log
cleanup-receipt.json
Evidence bundle SHA256:
ffdf5990041799c34f07e9aec662b10c820d4a7aa164fa70fac227d1d673729d.#220 perf-VM results (perf-test-03, 4 cores / 8 GB, Debian 13, kernel 6.12.113; kit
0d3b861d…+ orchestrator fixced872e68). 77 cases, one perf-lock hold each, every per-case state diff empty, exit 0. The run was serialised with other work on the VM through the shared lock. Medians over 3 repetitions:(The 8-flow loss in every seam comes from the bounded 64-packet window saturating a 4-core VM; compare the seams with each other, not against zero.)
PASS raw ICE connectedin both directions (11/11 and 11/12 accepted).Reading: this supports D313's seam C. Direct paths stay in the kernel at no measurable cost, and the shim is used only where the relay hop dominates. Its memory per pair is too small to matter at D170 scale.
Q8 (owner choice), size per candidate (x86_64 / aarch64): own PCP/NAT-PMP ≈ 9.4 / 13.0 KB (estimate) · igd-next+own 787 / 689 KB · portmapper 2.11 / 1.81 MB; own checklist ≈ 7.3 / 10.4 KB (estimate) · str0m ICE 208 / 184 KB (fits seam C, proven above) · rtc-ice 417 / 398 KB.
Raw JSONL kept outside the repo on the orchestrator host (
~/.cache/codex-scratch/mesh-220/evidence/). perf-test-03 before/after diff: empty.Starting slice 1 of the D313 order on branch
mesh-relay-220, basea4f15f8f7(origin/dev, D316). Build host 10by32-02-std (aarch64, root available for namespace tests). Read #220 (all 36 comments), D163/D166/D176/D185/D196/D197/D201/D313/D316,research/mesh-measure-220(8bb97ab88/ced872e68) and the earliermesh-relaycheckpoint commits1127aa19d(discovery book) and176aca846(discovery contract), which are not on any remote branch now; I will carry them forward rather than redo them. Slice plan follows in the next comment.Slice plan for the whole D313/D316 order (mesh-relay-220)
Sizes are estimates until measured with the pinned static recipe (D243: >16 KB needs a justification here, >64 KB goes to the owner). Every slice: atomic commits, fmt/clippy/tests per commit,
scripts/verify iteratewhile working,scripts/verify faston the head, numbers labelled by machine.1127aa19d) and its contract (176aca846);mesh::seam: probes sent from the kernel WG port through a send-onlyIPPROTO_RAWsocket, replies received on oneAF_PACKETsocket with a classic BPF filter (WG data never leaves the kernel); relay plugin runtime: relay service on quinn with ALPNinfraternal-relay/1, mTLS against the deployment CA, links formed only when both hosts ask for each other, zero-copy DATAGRAM forwarding of WG ciphertext, bounded tables, probe answers on the relay's own UDP port (first-byte demux in front of quinn); relay client;mesh::relay_pathshim: one loopback UDP socket per relayed peer, WG endpoint moved withClient::endpoint, failover to the next relayunshare -Urnm): EIM/EIF and EDM NATs, two relays, observation evidence Stable vs DestinationDependent, TCP stream through the relay with SHA-256 equal, relay killed mid-stream (failover to the second relay, gap reported), probes change no WG state and WG data never reaches the capture socket, stale mapping after a NAT rebind (generation retired, stream intact)infraternal relay up/down/statusreal (D198: nothing runs when off), server relay on by default (D163), agent node relays pairs from coordinator data (additivePeer.host, relay list), relay order by end-to-end RTT (D185); TLS on 443 with ALPN demux next to HTTPS + WebSocket upgrade, same link framing--jsonpunch_at, kernel WG handshakes punchHard-NAT prediction stays out until the Q4 counters trigger it.
Slice 1 contract notes (additive, no frozen field changes): relay link addresses are the host UUID from the verified
urn:infraternal:host:<uuid>SAN; a link forms only when both hosts ask for each other, so the relay needs no ticket in slice 1 and a node cannot inject into a pair that did not ask for it. Probe replies are authenticated with an HMAC keyed from the relay connection's TLS exporter, so an off-path sender cannot plant a mapping.Paused for the VM reboot. Head
4fe5cd912onmesh-relay-220(basea4f15f8f7). The slice plan is in the comment above.Done (pushed, each with fmt/clippy/crate tests green):
894faf492,8a1a054d0: the earlier discovery book and its contract, carried forward.39dd91062relay link/probe codecs (10 unit tests);e2b33c838relay service + client on quinn DATAGRAM, mutual-consent links, zero-copy forwarding, probe answers on the same UDP port (4 loopback integration tests).8b2256225kernel peer endpoint inCounters(it caught one out-of-bounds index on a short payload).513d2b9a3seam C:IPPROTO_RAWsend from the WG port and anAF_PACKETclassic-BPF tap.744c1bdfbrelay MTU discovery bound raised to 1472. Finding: with a cap of 1452, a full WG packet plus the link id is 2 bytes over quinn's DATAGRAM limit, so every full-size packet was dropped and relayed TCP stalled. A 1500-byte IPv6 underlay is still 2 bytes short; that goes to the low-MTU slice.c39e2cf80relay_path shim: lowest-index relay settling, failover, and a debounced first-session start by the lower host id. Finding: crossing initiations, or two initiations within the kernel's timestamp granularity, cost a full 5 s WG retry.4fe5cd912(WIP) fixture +scripts/mesh-relay-proof.py.Namespace proof, debug build, build host, aarch64, not perf evidence, passed 6/6 runs. EIM/EIF evidence
Stable, EDMDestinationDependent. The tap delivered only the 2 probe replies after 32 MiB relayed + 32 MiB direct. Link to first relayed handshake: 38–40 ms. 32 MiB relayed stream: SHA-256 equal, 0 too-large drops. Relay killed 1 s into a 384 MiB stream: detected in 3.0 s (idle timeout), longest gap 3.16 s, SHA-256 equal. With no relay left the pair reportsrelay: null(100% ping loss) and recovers about 2–3.5 s after a relay restarts. NAT rebind: old mapping retired, new one observed, 16 MiB SHA-256 equal. Host diff of links/netns/modules/net.* was empty on every run; everything ran inunshare -Urnm. Nothing of this job is running and no host namespace or link is left. bridge/veth were already loaded by another job before my last runs (modules_loaded_by_run: []).Next: replace the WIP commit with a proper one; release-build numbers; contracts/mesh-relay.md;
scripts/verify iterateandfast; size notes (expected 0 B shipped, since no binary feature enablesrelay/runtimeyet); delete the target directory.Starting J5 on branch mesh-relay-220 at WIP head
4fe5cd912d. Current origin/dev base is42ab330358. I read #220 and all 39 comments, D163/D176/D185/D196/D197/D201/D313/D316, and #262 §5. I will preserve the requested pre-rebase snapshot before splitting the WIP and rebasing.Contract proposal before the requested documentation/test edits:
Finding:
crates/plugins/relay/src/wire.rshas bounded decoder unit cases but no relay-specific fuzz target. I will add a parser fuzz target using the existing cargo-fuzz setup, without a new runtime dependency.Finding with evidence:
scripts/mesh-relay-proof.py:48hard-codesLABEL = "build host, aarch64, not perf evidence", while this task requires rerunning the proof oninfraternal-dev-01-compute01(x86_64). I will derive the displayed architecture from the running host so the new receipt cannot mislabel the run.Environment update at 2026-10-31 16:20 UTC:
infraternal-dev-01-compute01is unreachable (No route to host) and the owner is restarting it. Branchmesh-relay-220is rebased and pushed at9bb63f72415e020620de7da7cf9395a5fdcba0a6. Per the updated build-slot rules, I am moving architecture-neutral tests to dev-02 under~/infraternal-ops/build-slot; x86 static size and finalscripts/verify fastremain pending on compute01.Finding: the first queued dev-02 validation stopped at
cargo fmt --all --checkbefore tests ran. It reported one rustfmt layout difference in the new relay frame fuzz target. I applied the exact formatter layout tofuzz/fuzz_targets/relay_wire_decode.rs; the feature-on tests and clippy are now requeued under the dev-02 build slot. No behavior changed.Finding — header-only relay DATAGRAMs were accepted.
wire::splitreturnedSome((LinkId(9), []))for[00 00 00 09], although the frame contract requires ciphertext after the four-byte link ID. I changed the existing unit assertion first and reproduced the failure on dev-02 withcargo test -p infraternal-plugin-relay --lib --features runtime datagram_header_needs: it failed withleft: Some((LinkId(9), [])),right: None. I’m fixing the parser to reject header-only frames and adding that malformed case to the reproducible fuzz corpus.Follow-up evidence for the empty-payload finding: after freeing only this worktree's 1.9 GB incremental cache to recover from dev-02's full disk, the relay suite ran all 10 unit tests successfully. One loopback integration test then timed out because its first test payload is size 0 and it expects the four-byte link-ID-only frame to arrive. That assertion conflicts with the contract's requirement that the DATAGRAM carry a WireGuard packet. I’m updating the integration test to assert that this malformed frame is dropped and to keep the non-empty forwarding checks.
Finding from the first compute01
scripts/verify fastrun at608f95c0c69e0d4cb4d19a62abe994fab810eeea:delta: +1408 bytes). The aarch64 image measured 19,606,664 bytes against 19,605,896 (delta: +768 bytes).replay_size_reviewfailed in the shared test fixture because its fallback Unix socket path was 109 bytes, over Linux's 108-byte bound.The feature graph shows the new seam C module and
socket2dependency undermesh/agent, which is enabled in the default build. I have moved both under the opt-inmesh/relayfeature in the working tree. I am reproducing the socket-path failure on the cleanorigin/devworktree, then I will rerun the full gate with a short fixture-state root and recheck both static sizes.Follow-up evidence for the fixture-path failure: the clean baseline worktree on compute01 is detached at
42ab330358eb06d47acade8d0f2c35b9225af74b. Calling the unchanged baselinescripts/agent_fixture.py::fixture_state("rr257")with both test-state overrides unset produced the same error:ValueError: test state path is too long for an AF_UNIX socket after fallback: 109 bytesThis reproduces the failure on unmodified
origin/dev; the final gate will use the fixture's supportedINFRATERNAL_AGENT_TEST_STATE_ROOT=/tmp/mr220override. No product code change is needed for the test-path issue.Progress at pushed head
0d1142b0bae22e9f794689bd346d162702970b59:too_large_A=0,too_large_B=0; the 16 MiB rebind stream matched;host_state_diffwas empty withunchanged: true. The target and temporary/tmpcopies were removed; dev-02 is back to 25 GB free.scripts/verify faston compute01 is queued under build-slot because all four slots are occupied. It is running withINFRATERNAL_AGENT_TEST_STATE_ROOT=/tmp/mr220to avoid the 109-byte fallback socket path reproduced on unmodifiedorigin/dev. Log:/home/claude/.cache/codex-scratch/mesh-relay-220/evidence/verify-fast-final.log.Next: finish the gate, verify both static size rows and exact D243 deltas, then post the finish comment.
Finding/update: after the branch was rebased to
42ab330,origin/devadvanced to105b1a28(including the pinned Nix dev shell at57491e918). The first queued final-gate attempt also enteredbuild-slotwithout that dev shell, so Rust checks reportedcargo: command not found; it did not produce a valid final gate. I am preserving the current pushed head, rebasing onto currentorigin/dev, and will rerun the final gate throughnix develop -c ~/infraternal-ops/build-slot ...on compute01.Progress: rebased and pushed
mesh-relay-220at553a8709bbdd66918d0bad2353881154be1b1d01, based on currentorigin/dev105b1a28d3b3ea86487c457249ab3f953a4c27d3. Preserved the pre-rebase pushed head atwip/mesh-relay-220-pre-final-rebase; the original requested WIP snapshot remains atwip/mesh-relay-220-pre-rebase. Compute01's worktree is on the final head.scripts/verify fastis now running vianix develop -c env ... ~/infraternal-ops/build-slot ./scripts/verify fast; log:/home/claude/.cache/codex-scratch/mesh-relay-220/evidence/verify-fast-553a870.log. I will report the verbatim gate summary and exact D243 size deltas after it finishes.Finding during the final-head namespace rerun: invoking the proof from the private worktree path with outer sudo leaves the host snapshot unchanged, but the inner
unshare -Urnmprocess cannot traverse/home/claude/worktrees/...after it enters the user namespace (/usr/bin/python3: can't open file ...: [Errno 13] Permission denied). I will copy only the proof script and its built fixture into a short, world-readable/tmppath, rerun throughbuild-slotas root, and remove the copies afterward.Progress at
553a8709: final-head relay tests passed on dev-02 withtest-supportenabled (10 unit + 4 QUIC loopback tests) and clippy passed. The root namespace proof is queued through build-slot after I removed this worktree's target and staged only the executable fixture/script under/tmp; dev-02 has since fallen to 16 GB free, below its 25 GB slot threshold. Compute01's finalscripts/verify fastis also queued while all four slots are occupied. Logs are under/home/claude/.cache/codex-scratch/mesh-relay-220/evidence/on the respective hosts.Capacity update: dev-02 free space has fallen from 16 GB to 9.6 GB (96% used), so
build-slotcontinues holding the final-head namespace proof below its 25 GB threshold. This job'starget/is removed; only the 127 MB copied fixture/script needed by the proof remains under/tmp/mr220-final-553a870. I am not touching other jobs' files. Compute01'sscripts/verify fastremains queued behind its four active slots. The final proof and x86 gate are still pending.Final-head namespace proof passed at
553a8709bbdd66918d0bad2353881154be1b1d01on dev-02 (aarch64, functional evidence; not perf evidence), as root through build-slot. EIM/EIF observedStable; EDM observedDestinationDependent. The 32 MiB relayed stream had matching SHA-256 andtoo_large_A=0,too_large_B=0. A relay killed during a 384 MiB stream failed over with matching SHA-256; longest gap 3,163.8 ms, both ends on relay 2 after 3,004.5 ms. Traffic recovered 2,344.8 ms after relay restart. NAT rebind retired the old mapping and delivered a 16 MiB stream with matching SHA-256. The proof exited 0;host_state_diffwas empty withunchanged: true, and every module loaded by the run was unloaded (still_loaded: []). Full receipt:/home/claude/.cache/codex-scratch/mesh-relay-220/evidence/mesh-relay-proof-553a870.logon dev-02.Progress: namespace proof and feature-on relay tests are complete at
553a8709. The required compute01 final gate is still waiting in~/infraternal-ops/build-slotafter 21 minutes because all four slots remain occupied; its log contains only the queue message so far. It is running under the current branch's Nix dev shell and the/tmp/mr220fixture-path override. I will wait for the slot and post the exact D243 rows plus the verbatim gate summary when it exits.Progress after 30 minutes: the final compute01 gate remains queued, with all four build slots still occupied. The clean implementation is pushed at
553a8709bbdd66918d0bad2353881154be1b1d01; namespace proof, 10 unit + 4 loopback tests, and feature-on clippy are complete. I am continuing to wait for compute01 to runscripts/verify fast; no source changes are pending.Queue update: stopped the old starved waiter by its unique process group
3981575;ps -g 3981575confirmed no remaining process. Relaunched the unchangedscripts/verify fastcommand through the updated FIFO build-slot wrapper. The new log reportsbuild-slot: [23:17:20] all 4 slots busy; queue position 1, waiting...; no other jobs were stopped. New log:/home/claude/.cache/codex-scratch/mesh-relay-220/evidence/verify-fast-553a870-fifo.log.Finding from the FIFO-run final gate at
553a8709(base105b1a28): the x86_64 static artifact is 21,433,008 bytes against D243's 21,431,472-byte cap,delta: +1536 bytes. The default relay CLI compile-out check and clippy passed; the gate is continuing through the aarch64 static build. I am tracing the remaining shipped bytes and will report the aarch64 row before adjusting the feature boundary.Finding while tracing the +1,536-byte x86 D243 delta:
infraternal-mesh::discoveryis exported unconditionally from the shared mesh crate, while current references are limited to the relay namespace fixture and its dedicated tests. The relay fixture is feature-gated, but this public module is still part of the default mesh dependency graph. I will gate the discovery book and its test target behind the relay feature, enable that feature only frominfraternal-plugin-mesh/relay, and update the contract to name that boundary; then I will verify feature-on behavior and rerun D243.Finding from the FIFO run at
553a8709(rebased on105b1a28): the D243 static rows were x86_64 21,433,008 B / cap 21,431,472 B (+1,536 B) and aarch64 19,606,664 B / cap 19,605,896 B (+768 B). The default image still includedinfraternal-mesh::discovery, which has no default-path consumers. I gated it behindinfraternal-mesh/relay, enabled byinfraternal-plugin-mesh/relay, in pushed commit2813200b0rather than widening either cap.The same gate had three
remote_native_tests::terminal::gone_reviewfailures withpath must be shorter than SUN_LEN. Those tests useINFRATERNAL_TEST_STATE_ROOTfromcrates/infraternal-agent/src/lib.rs; the separateINFRATERNAL_AGENT_TEST_STATE_ROOTonly sets the fixture helper's root. Re-running the fivegone_reviewtests on dev-02 withINFRATERNAL_TEST_STATE_ROOT=/tmp/mr220-testspassed 5/5. The final x86 gate now sets both short roots. I will attach its transcript and final D243 rows with the finish comment.Progress at pushed head
2813200b0f03159733ab744a237025bd70d3075d(base105b1a28d3b3ea86487c457249ab3f953a4c27d3):infraternal-mesh24 unit/property tests; relay plugin 10 unit tests + 4 QUIC loopback integration tests; feature-on clippy; and the relay fixture build.Stable, EDMDestinationDependent; 32 MiB relayed stream SHA-256 equal withtoo_large_A=0,too_large_B=0; 384 MiB failover stream SHA-256 equal with 3,120.1 ms maximum gap; 16 MiB rebind stream SHA-256 equal and old mapping retired.host_state_diffwas empty withunchanged: true; every module loaded by the run was unloaded.scripts/verify fastis queued on compute01 through the FIFObuild-slot, with both supported short fixture roots set. It started at queue position 6; no other job was stopped.Logs are under
/home/claude/.cache/codex-scratch/mesh-relay-220/evidence/on the respective VMs. I will attach the proof and gate transcripts with the finish comment.Progress at pushed head
2813200b0f03159733ab744a237025bd70d3075d: feature-on tests/clippy, fuzz run, and final-head namespace proof have passed; the evidence is attached to earlier comments. The required x86scripts/verify fastis still live under the FIFO wrapper on compute01, queue position 6 for about 30 minutes. It has not started, so there is no final gate output yet. Log:/home/claude/.cache/codex-scratch/mesh-relay-220/evidence/verify-fast-2813200.log. The branch is clean and pushed; I am retaining the queue ticket.Finding from the active compute01
verify fastat2813200b0: I confirmedINFRATERNAL_TEST_STATE_ROOT=/tmp/mr220-rustandINFRATERNAL_AGENT_TEST_STATE_ROOT=/tmp/mr220. On compute01/tmpistmpfs, while/home/claude/worktrees/mesh-relay-220and the home cache are on/dev/sda1(ext4). The seven failures are in restart/recovery executable-image fixtures; the transcript showsCrossesDevices(EXDEV) for hard-linking across those filesystems andPermissionDeniedin the related executable trust-path checks. This is a test-root placement issue, not a relay code failure. I am letting this run finish to retain its D243 static rows, then will rerun the full gate with both roots below/home/claude/.cacheso fixture hardlinks stay on ext4.Finding from the fresh
2813200b0static build on compute01: x86_64 measured 21,430,960 B against the existing 21,431,472 B D243 ceiling (delta: -512 B).scripts/verify fastcorrectly reports that shrink as a ratchet failure because D243 has no hidden slack. This is consistent with the relay runtime/discovery being gated off in the default build. The aarch64 row is still compiling; after it completes I will set both D243 ceilings to the fresh measured artifacts, then run the corrected-root full gate again.Progress:
origin/devadvanced to0d96a74287727946779270a12662dfc6067ede25while the compute01 gate was running. I preserved the previous pushed head atwip/mesh-relay-220-pre-dev-advance-rebase, rebased cleanly, and force-with-lease pushedmesh-relay-220at8872ba42c0b797f7e543eae80f067a48111622faon the new base. The currently active size run is still compiling its prior2813200b0checkout; I will use it only as preliminary measurement evidence, then run the final gate with home-cache fixture roots against the rebased head. The namespace proof will also be rerun at the rebased head before the finish comment.Preliminary compute01 gate at old pre-rebase head
2813200b0completed. It is not the final result: workspace tests failed because both fixture roots were on/tmp(tmpfs), and the exact-size checker rejected the unratcheted D243 rows. Fresh static rows were x86_64 21,430,960 B / cap 21,431,472 B (-512 B) and aarch64 19,608,712 B / cap 19,605,896 B (+2,816 B). The default feature graph on the rebased checkout8872ba42chasinfraternal-plugin-meshfeaturesagent/server,infraternal-meshdefaultonly, andinfraternal-plugin-relaydefaultonly; neither meshrelaynor relayruntimeis enabled. I will record the measured exact ceilings without widening them, then rerun the full gate on8872ba42cwith both roots under/home/claude/.cache.Final-head namespace proof rerun passed at
8872ba42c0b797f7e543eae80f067a48111622faon dev-02/aarch64 as root throughbuild-slot(functional evidence, not perf evidence). Exact result: EIM/EIF observationStable; EDM observationDestinationDependent; relayed 32 MiB stream SHA-256 equal,too_large_A=0,too_large_B=0; relay-kill 384 MiB failover stream SHA-256 equal with maximum gap3164.3 ms; relay restart recovered in2188.9 ms; NAT rebind retired the old mapping and delivered a 16 MiB stream with SHA-256 equal.still_loaded: [],host_state_diffempty withunchanged: true,exit_code=0. Receipt:/home/kayg/.local/state/infraternal-orchestrator/J5-mesh-relay-220/evidence/mesh-relay-proof-8872ba.log.Fresh feature-on validation at rebased head
8872ba42c0b797f7e543eae80f067a48111622faon dev-02 passed throughbuild-slot:infraternal-mesh --features relay(24 tests including discovery properties), relay unit tests (10/10), and QUIC loopback relay integration tests (4/4). The parser's bit-corruption and malformed-frame property cases pass. Receipts:/home/kayg/.local/state/infraternal-orchestrator/J5-mesh-relay-220/evidence/mesh-property-tests-8872ba.logandfeature-tests-8872ba.log. The target directory was removed afterward; dev-02 is back to 45 GB free.Queue update for the corrected final gate at
8872ba42c0b797f7e543eae80f067a48111622fa: the compute01scripts/verify fastticket is live in the updated FIFObuild-slotat position 13 since 01:01:34 UTC. Both fixture-root variables are confirmed in the waiting process and point to/home/claude/.cache. No build has started yet; I have kept the ticket and have not stopped other jobs. Final-head namespace proof and feature-on tests are complete; the x86 gate remains the outstanding step.D243 ratchet committed and pushed as
df8f69b3092f1077eafd4d154e4d6e3047929afbonmesh-relay-220. The new exact limits match the preliminary static artifacts: x86_64 21,430,960 B (-512 Bvs the prior ceiling); aarch64 19,608,712 B (+2,816 B, under 16 KiB).scripts/test-static-size.pypassed the exact-cap, +1-byte rejection, shrink-ratchet and unsupported-target cases for both targets. The full corrected-rootscripts/verify fastremains queued on compute01 at FIFO position 13; it will run the committed content (the compute worktree already has these exact docs/script changes).FIFO progress: the current live-ticket count is 6 ahead of the final gate (initially position 13); the wrapper log prints only the first position, so it still contains the original message. The final gate has not acquired a slot yet; its ticket is live and retained.
Queue progress: 5 live tickets remain ahead of the final compute01 gate. Its FIFO ticket is still live; no final build/test stage has started yet. The log file remains
/home/claude/.cache/codex-scratch/mesh-relay-220/evidence/verify-fast-8872ba.log.Queue progress: 4 live FIFO tickets remain ahead of the corrected final gate. The gate ticket is still live; it has not started yet.
FIFO progress: 3 live tickets remain ahead of the final compute01 gate; its ticket is live and retained.
FIFO progress at 2026-11-01 01:35 IST: the previous racing waiter was replaced with a live ticket on the FIFO build-slot. It is currently ticket 3 (two live tickets ahead); the final scripts/verify fast build has not started yet. Remote worktree is clean at
df8f69b309. I am retaining this ticket and will record the exact final gate output and D243 rows when it runs. Log: /home/claude/.cache/codex-scratch/mesh-relay-220/evidence/verify-fast-8872ba.log.Compute01 milestone at 2026-11-01 01:41 IST: the FIFO ticket acquired a slot and scripts/verify fast is running against
df8f69b309. The current transcript has reached clippy after Rust tests and the plugin compile-out row; no final gate result is available yet. Full live log: /home/claude/.cache/codex-scratch/mesh-relay-220/evidence/verify-fast-8872ba.log.Finished #220 slice 1 at
df8f69b3092f1077eafd4d154e4d6e3047929afb, clean and pushed onmesh-relay-220, rebased ontoorigin/dev0d96a74287727946779270a12662dfc6067ede25. The WIP is split into atomic commits; the pre-rebase snapshots remain pushed underwip/mesh-relay-220-pre-rebase,wip/mesh-relay-220-pre-final-rebase, andwip/mesh-relay-220-pre-dev-advance-rebase.contracts/mesh-relay.mdrecords control and DATAGRAM framing, seam C socket ownership, slice-1 ordered relay/failover rules, and the D313 Q6 status. TLS+ALPN on 443 and the WebSocket-on-443 fallback are required for the first release but are not implemented in this slice; HTTP CONNECT remains excluded.The default feature graph does not enable
infraternal-plugin-mesh/relayorinfraternal-plugin-relay/runtime; theCLI plugin compile-outgate passed. Relay runtime code adds 0 B to the default shipping image. The final static rows and D243 boundary checks fromscripts/verify fastare:Against the previous D243 ceilings (21,431,472 / 19,605,896 bytes), the exact cap deltas are −512 B x86_64 / +2,816 B aarch64, both below 16 KiB. The full final gate ran on
claude@infraternal-dev-01-compute01throughbuild-slot. Verbatim Rust summary:TypeScript protocol output:
39 pass,0 fail,50325 expect() calls; the Rust/TypeScript stream differential reported155 streams, 16716 schedules, zero divergences. The full transcript is attached.The final-head namespace proof on dev-02/aarch64 was run as root through
build-slot(functional evidence, not performance evidence). Quoted results:Feature-on evidence: mesh/discovery property suite 24 passed; relay unit tests 10/10; QUIC loopback integration tests 4/4. The relay frame parser fuzz target completed 10,000 inputs; this toolchain build lacked coverage instrumentation, as noted in the attached log.
Perf-VM evidence already recorded above on #220: seam B was within about 3–10% of the kernel baseline; seam A used about 2× CPU per Gbit/s and added 300–600 µs one-way p50; shim state was about 270 B per relayed pair at 1,000 pairs.
The compute01 target and per-test roots were removed after the gate; the remote worktree is clean at the pushed head. This issue remains open.
J43 review started on branch
review-mesh-relay-220, based ondf8f69b3092f1077eafd4d154e4d6e3047929afb(targetmesh-relay-220). I will read the contract, decisions, implementation, and measurements, then record evidence-backed findings and failing regression tests on the review branch.Checkpoint: review branch review-mesh-relay-220 is pushed at
fc04bdb83from basedf8f69b309. It adds two intentionally failing regressions for reconnect link-request state and same-second WireGuard handshake freshness. Next: rerun feature-on suite on dev-02, complete the root namespace proof, then run scripts/verify fast on compute01. Existing failure logs: /tmp/j43-stale-link-red.log and /tmp/j43-same-second-red.log; remote copies are in ~/.cache/codex-scratch/j43-mesh-relay-220/evidence/.Review checkpoint: review-mesh-relay-220 is pushed at
b24f68896fromdf8f69b309. The initial reconnect regression was a false positive: the contract preserves a still-connected peer’s request after the other host leaves, so I removed it. The same-second handshake regression is confirmed: direct_failed compares second-resolution timestamps with <, so an old handshake in the endpoint-install second suppresses relay fallback. Next: rerun feature-support tests, complete the namespace proof, and run verify fast on compute01.J43 independent review findings — target
df8f69b309; review branch review-mesh-relay-220 @61f689879.P0: none established in the source paths reviewed. The WireGuard shim path carries kernel ciphertext; the relay forwards DATAGRAM bytes unchanged after link ownership checks. Admission uses a verified host certificate plus the required membership/revocation callback. Authenticated address probes are fixed-size request/reply, so no open reflector was demonstrated.
P1 — crates/plugins/mesh/src/relay_path.rs:69-70. direct_failed compares only handshake_secs < endpoint-install seconds. A stale successful handshake in the same second compares equal and suppresses relay fallback. The regression at relay_path.rs:690-704 fails under relay,test-support. Mesh plugin result: 70 passed, 1 failed (this regression), 14 ignored. Verdict: hold merge until fixed.
P2 — crates/plugins/relay/src/relay.rs:35-51 and crates/plugins/relay/src/tls.rs:31-37. The defaults allow 4,096 hosts, below D170's 10,000-node headroom, with 1,024 pending requests per host. Configured receive/send DATAGRAM buffers are 512/256 KiB per connection, or 750 MiB allowance at 1,000 connections and 3 GiB at the 4,096-host cap before link tables. Those are caps, not measured RSS; the review could not complete abuse-scale memory measurement.
Relay tests on dev-02 passed with test-support: 10 unit and 4 loopback tests. Mesh discovery passed. D313's quality-switch rule (max(5 ms, 15%) across five samples spanning >=3 seconds, 10-second dwell) is not evaluated by the reported 3,164.3 ms relay-kill gap or 2,188.9 ms restart recovery. The kill gap is ~164 ms over the 3-second QUIC idle timeout. MTU 1,472 covers IPv4/1500; IPv6/1452 remains 2 bytes short and is documented as deferred.
Blocked checks: dev-02 SSH stopped responding after the relay fixture build was launched, so the root namespace proof and fixture cleanup could not be verified. compute01 SSH did not return, so scripts/verify fast and x86_64 size rows were not captured. No coverage-guided fuzz run: cargo-fuzz/nightly were absent in the available Nix dev shell. See findings.md on the review branch for details and evidence paths.
J43 recovery update at review branch
71f84a007: the staged root namespace proof completed on dev-02 through sudo/build-slot. It reported matching hashes for the relayed 32 MiB stream, the 384 MiB relay-kill stream (max gap 3,121.2 ms), and the 16 MiB rebind stream;still_loaded: []andhost_state_diffwas empty (unchanged: true). Temporary /tmp staging was removed. This supersedes my prior comment saying the proof was blocked. The required compute01scripts/verify fastis now running through FIFO build-slot with both fixture roots under/home/claude/.cache; its log is/home/claude/.cache/codex-scratch/j43-mesh-relay-220/evidence/verify-fast.log.Compute01 gate adjustment: I stopped only the isolated J43 gate process group after noticing scripts/verify does not set Cargo parallelism. The partial log is preserved as verify-fast-default-jobs.partial.log. The required FIFO build-slot run is relaunched with CARGO_BUILD_JOBS=4 and both short fixture roots on /home/claude/.cache; the current log is verify-fast.log.
J43 gate progress at review branch
71f84a007: compute01scripts/verify fasthas passed clippy and the full workspace tests (test: 1095s), then the SSH inode fixture was skipped because the filesystem did not reuse a released inode after 256 attempts. Static path checks passed. The x86_64 artifact row isPT_INTERP: absent; size: 21430960 bytes; cap: 21430960 bytes; delta: +0 bytes. The aarch64 static build is active through FIFObuild-slot; final gate exit and ARM row remain pending. Log:/home/claude/.cache/codex-scratch/j43-mesh-relay-220/evidence/verify-fast.log. Next: collect the ARM row/exit, update and push the review report, then post the final P0–P3 verdict.J43 final independent protocol/security review. Target:
df8f69b3092f1077eafd4d154e4d6e3047929afbonmesh-relay-220(base0d96a7428). Review branch:review-mesh-relay-220@e1a4676cd. Currentorigin/devisf4b61cd6c; this review remains pinned to the requested target.Verdict: HOLD merge pending the P1 direct-fallback fix. Full report:
findings.mdon the review branch.crates/plugins/mesh/src/relay_path.rscarries kernel WireGuard ciphertext through the shim;crates/plugins/relay/src/relay.rs:261-283checks link ownership and forwards the DATAGRAM unchanged. No mesh key or decryption path is present. Relay clients authenticate against deployment CA roots; the exact host URI SAN is passed to the required membership/revocation callback (crates/plugins/relay/src/tls.rs:86-143,crates/plugins/relay/src/relay.rs:56-57,455-461). Any admitted same-mesh host may use a configured relay, matching D313 Q3; a different mesh cannot authenticate. Probe replies require an authenticated live-connection exporter key and are equal-size 84-byte replies, so I found no open reflector or cross-mesh capacity path.crates/plugins/mesh/src/relay_path.rs:69-70.direct_failedcompares second-resolution handshake time with<. A handshake before endpoint installation in the same second compares equal and suppresses relay fallback. Regressiona_same_second_handshake_cannot_prove_it_followed_the_endpoint_updateatrelay_path.rs:690-704fails. Feature-on mesh test result: 70 passed, 1 failed (this regression), 14 ignored. Relay tests passed 10 unit + 4 QUIC loopback; discovery passed 24.crates/plugins/relay/src/relay.rs:35-52,crates/plugins/relay/src/tls.rs:31-37. Defaults permit 4,096 hosts and 1,024 pending requests per host. QUIC receive/send caps total 750 MiB configured queue allowance at 1,000 connections and 3 GiB at 4,096, before link tables. This is a configured maximum, not measured RSS. It is finite, but below D170's 10,000-node headroom test target and lacks abuse-scale RSS evidence.Root namespace proof ran as root on dev-02 through
cua/build-slot. J43 measured matching hashes for 32 MiB relayed, 384 MiB relay-kill (max gap 3,121.2 ms), and 16 MiB rebind streams (old mapping retired);still_loaded: [], with links/namespaces/modules/sysctls unchanged. J5's final proof measured 3,164.3 ms kill gap and 2,188.9 ms restart recovery. Kill gaps are 121–164 ms beyond the 3-second idle timeout. These outage measurements do not test D313's quality-switch rule (win >= max(5 ms, 15%), five samples spanning >=3 s, 10 s dwell). The contract disclaims packet continuity on relay loss and defers the byte-preserving selector. MTU 1,472 fixes IPv4/1500; IPv6/1452 remains two bytes short, explicitly deferred. D313 Q6's TLS+ALPN and WebSocket fallbacks on 443 are absent and the contract says this slice does not complete UDP-blocked first-release support.Feature-off compile-out passed:
infraternal-plugin-mesh/relayandinfraternal-plugin-relay/runtimeare disabled; runtime adds 0 B to the default image. Static deltas against previous D243 ceilings are -512 B x86_64 / +2,816 B aarch64; D243 approval caps have since been removed.scripts/verify faston compute01 through FIFObuild-slot: exit 0. Verbatim summary:Gate transcript:
/home/claude/.cache/codex-scratch/j43-mesh-relay-220/evidence/verify-fast.log; namespace proof:/home/claude/.cache/codex-scratch/j43-mesh-relay-220/evidence/root-proof.log. Build targets and fixture roots were removed; evidence logs were retained. #220 remains open.J49 starting on branch
mesh-relay-220at targetdf8f69b3092f1077eafd4d154e4d6e3047929afb, currently based onorigin/dev 0d96a7428. I will rebase ontoorigin/dev f4b61cd6c3f52078cad7573c5367d7a6e75f84f0before code changes.Contract proposal before implementation:
start_handshake; its nonzero check preserves a live cryptographic session and does not decide endpoint freshness.Bytesownership through Quinn's send queue, and drop DATAGRAMs when either budget is full.contracts/mesh-relay.md, then measure relay RSS with many pairs flooding on the perf VM before and after usingperf-run --root.The owner removed D243 approval size caps. I will still report the static image delta against the rebased dev baseline.
P1 reproducer confirmed on the rebased target. I copied J43's
a_same_second_handshake_cannot_prove_it_followed_the_endpoint_updatetest into the mesh path tests and ran it onclaude@infraternal-dev-02-stdthroughbuild-slotundernix develop. Result: 0 passed, 1 failed, exit 101. It fails becausedirect_failedcompareshandshake_secs < since_unix_secs; an equal-second handshake is accepted as fresh even though endpoint-install ordering is unknown. I will use the conservative equality rule. The separatestart_handshakenonzero-timestamp check only preserves an existing WireGuard session and is not a freshness comparison.J49 progress: P1 is committed and pushed at
bc3d654e5. The J43 regression failed at the ambiguous same-second assertion before the <= freshness change, then passed 1/1 on dev-02; same-second-green.log records the passing line. I am compiling the isolated relay flood RSS benchmark on compute01 before changing queue limits. Build target/log: ~/.cache/codex-scratch/j49-mesh-relay-220/{target,baseline-build.log}.J49 WIP checkpoint pushed at
700e9bb83(after P1 commitbc3d654e5). The feature-on memory regression is red because QueueBudget/queue_forward_datagram are not implemented yet. The RSS harness had one compile error in paired notice reads; that is fixed with split_at_mut. Its pre-change release build is queued on compute01 at build-slot position 9; current log: ~/.cache/codex-scratch/j49-mesh-relay-220/baseline-build.log. Next: complete baseline RSS, implement/account the bounded shared buffers, then run the same flood after the change.J49 follow-up findings and evidence:
<=fix passed J43’s stale same-second regression but falsely treated a fresh same-second handshake as stale. The root namespace proof timed out waiting for peer C’s direct event; WireGuard reported a nonzero handshake timestamp in the endpoint-install second, C had no active relay, and the fixture entered the relay path. I changed the comparison to use the kernel’s handshake seconds and nanoseconds against the post-install timestamp. The stale, fresh same-second, and exact-equality cases pass 4/4 on dev-02; the full feature-on mesh suite passes 73 tests with 14 ignored. The failed proof’s host-state check was still clean. Root proof rerun is pending.start_handshakechecks only whether any session exists before resetting WireGuard state; it does not compare handshake freshness. The other handshake reads report presence or age and do not compare against endpoint installation.infraternal-perf-01-backup01, Debian 6.12.113, Ryzen 7 3700X. Pre-budget relay binary from the baseline worktree;perf-run --rootwith an isolated network namespace, 256 pairs / 512 hosts, 131,072 DATAGRAM attempts. Idle relay RSS/HWM was 26,704 KiB; flood RSS/HWM peaked at 33,772 KiB, +7,068 KiB over idle. Flood phase 2,164 ms; 60,158 sends accepted by clients, 70,914 blocked at their queues. No process exceeded 5% CPU in the pre/post quiet checks; steal was 9/1,478 jiffies.tcis unavailable, so this run used unshaped loopback.Evidence:
/home/claude/.cache/codex-scratch/j49-mesh-relay-220/{before.log,perf-before/},mesh-feature-tests.log, andfreshness-green.log. The final memory table and after-budget measurement will follow.P1 verification update: the release root namespace proof now passes with the precise timestamp comparison. A→C stays direct and transfers 32 MiB with a matching SHA-256; the 32 MiB relay stream, 384 MiB relay-failover stream, and 16 MiB rebound stream also match hashes. The proof reports
still_loaded: [], an emptyhost_state_diff, andunchanged: true. Receipt:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/root-proof-round2.log.The contract now records the seconds+nanos ordering rule. I found no second-granularity endpoint freshness comparison elsewhere:
start_handshakeonly checks whether a session exists, while presence and status code read handshake age/state. P1 is ready to commit; P2 after-budget RSS and the final fast gate remain.J49 progress on
mesh-relay-220@fc1ffa97f(rebased ontoorigin/devf4b61cd6c):still_loaded: [], an emptyhost_state_diff, andunchanged: true. Selector review found no other endpoint freshness comparison using seconds-only ordering.Bytesownership with reservations held through Quinn, and DATAGRAM drop/count under pressure.contracts/mesh-relay.mdrecords the 8/128 KiB relay receive/send settings and aggregate bounds.infraternal-perf-01-backup01(Debian 6.12.113, Ryzen 7 3700X): 256 pairs / 512 hosts, 131,072 attempts, idle RSS/HWM 26,704 KiB, flood peak RSS/HWM 33,772 KiB (+7,068 KiB), 2,164 ms. Quiet checks passed;tcis unavailable, so loopback was unshaped. Logs:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/before.logandperf-before/.scripts/verify fastare queued through compute01 FIFObuild-slot(last observed positions 13 and 14). Logs:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/after-build.logandverify-fast.log.Next: collect post-budget RSS and the final gate/static-size delta, then post the round-2 per-finding summary and before/after memory table. Issue remains open.
J49 progress on
mesh-relay-220@fc1ffa97f:still_loaded: [], emptyhost_state_diff,unchanged: true).before.logandperf-before/: idle RSS/HWM 26,704 KiB; flood peak RSS/HWM 33,772 KiB (+7,068 KiB); 131,072 attempts in 2,164 ms.scripts/verify fastremain queued through compute01 FIFObuild-slot. The current queue has two earlier tickets; J49 is next for the build and next after that for the gate. Logs:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/after-build.logandverify-fast.log.Next: collect the post-budget RSS result, quote the fast-gate output and report D243 static-size deltas, then post the final P1/P2 summary and before/after memory table. No code changes since
fc1ffa97f; issue remains open.J49 gate finding on the current head
fc1ffa97f:scripts/verify fastreached clippy and failed to compileinfraternal-plugin-meshtest code atcrates/plugins/mesh/src/node_actions.rs:841: theCountersinitializer lackedhandshake_nanos. The P1 change had made that field unconditional while the initializer still included it only undertest-support.21f5bb760as a WIP commit.handshake_nanos, its parser and timestamp tests are now gated on therelayfeature, and the node-action literal uses the same feature gate. The relay-enabled path retains the precision comparison; ordinary agent builds avoid the extra field and parsing.fc1ffa97fand will finish before the worktree is updated. Next: rerun feature-on mesh tests andscripts/verify faston21f5bb760, collect the D243 size delta, then post the final per-finding summary and RSS table.Gate log:
/home/claude/.cache/codex-scratch/j49-mesh-relay-220/verify-fast.log. Issue remains open.J49 checkpoint pushed at
b4c9ba1aeonmesh-relay-220.#[cfg]to an assignment inside a match arm. I moved the gate onto feature-specific match arms; relay builds still parse seconds+nanos, feature-off builds keep seconds-only counters.nix developandbuild-slot, mesh tests passed with relay off: 64 passed, 14 ignored; relay on: 73 passed, 14 ignored. Relay tests passed 12 unit and 4 loopback tests, including shared-budget and relink-cap regressions.still_loaded: [], empty host-state diffs, andunchanged: true.scripts/verify fastrun remains active againstfc1ffa97f; its log is/home/claude/.cache/codex-scratch/j49-mesh-relay-220/verify-fast.log. I have not changed that worktree while it runs.Next: collect the old gate result, update the compute01 worktree to this head, rerun the required fast gate and record the D243 size delta, then split/squash the round-1 fixes into clean P1 and P2 commits and post the final per-finding memory summary.
J49 gate restart: the earlier compute01 process had exited or been removed before producing a summary; there was no matching J49 process and the log ended during OpenSSL generation. The compute01 worktree was clean, so I fetched
b4c9ba1aeand reset that worktree safely. It is still based on the requestedf4b61cd6c(git merge-baseconfirmed exact match).A fresh
scripts/verify fastis now running throughbuild-sloton compute01. Log:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/verify-fast-r2.log. I will report its actual output and D243 static-size delta; the old partial log is retained as evidence of the abandoned run.J49 round-1 fixes pushed on
mesh-relay-220at04e9e1241, based on the requestedorigin/devcommitf4b61cd6c. The final tree is identical to the tested checkpoint tree. Clean commits:1fc88b2cb(P1) and04e9e1241(P2).P1 — stale same-second handshake blocked relay fallback
direct_failednow compares WireGuard handshake seconds and nanoseconds with the endpoint-installSystemTime. Equal or earlier timestamps allow relay fallback; a later handshake in the same second keeps the direct path. J43'sa_same_second_handshake_cannot_prove_it_followed_the_endpoint_updatepasses, alongside fresh-same-second and exact-equality cases. A selector search found no other seconds-only endpoint-freshness comparison;start_handshakeonly checks whether a cryptographic session exists. Precision parsing/storage is relay-feature-only, keeping feature-off counter layout unchanged.P2 — 3 GiB configured relay DATAGRAM queue allowance
The relay now enforces a 64 MiB global forwarding budget and 256 KiB per-pair cap, charging 1 KiB units after a 128-byte per-frame overhead. Reservations travel with shared
Bytesstorage through Quinn. Relay receive queues are 8 KiB per connection; the 128 KiB send ceiling is bounded in aggregate by the shared reservations. On pressure or a full send queue, DATAGRAMs are dropped and counted, consistent with their unreliable delivery semantics. At 4,096 hosts, the contract bounds configured relay receive plus forwarding queues to 96 MiB, excluding fixed connection/control/link-table state.Perf VM
infraternal-perf-01-backup01, sameperf-run --rootnamespace workload, 256 pairs / 512 hosts / 131,072 attempts:Both runs passed the perf-run quiet check;
tcis unavailable on the VM, so loopback was unshaped. Evidence logs:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/{before.log,after.log}andperf-before/,perf-after/.Feature checks on dev-02 through
build-slot/nix develop: mesh feature-off 64 passed / 14 ignored; mesh relay-on 73 passed / 14 ignored; relay tests 12 unit and 4 QUIC loopback passed, including both budget regressions. The root namespace proof reports matching direct, relay, failover and rebound hashes,still_loaded: [], empty host-state diffs andunchanged: true(receipt:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/root-proof-round2.log).The fresh
scripts/verify fastis queued on compute01 throughbuild-slot(initial position 11). I will add its verbatim output and the static D243 size delta when it completes. D243 approval caps were removed; I am reporting the measured delta, not stopping on a cap.J49 checkpoint remains clean and pushed at
04e9e1241; no source changes are pending. The compute01 gate ticket has moved to FIFO position 6 but has not acquired a slot yet. Its log currently contains only the initial queue line:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/verify-fast-r2.log. The branch'smesh-relay-220tree is unchanged from the tested checkpoint. I will post the gate summary and static-size delta when the queued run finishes.J49 resuming from the pushed WIP.
mesh-relay-220has been rebased onto currentorigin/dev37bbda45554f6e603a6856fc23319e17d8a1b14fand force-pushed with an explicit lease at960a2a364d90face69abcd0d55025e612449d3f7. The P1 and P2 fixes remain separate commits (964607892and960a2a364).Next I am rerunning feature-on tests and the root namespace proof on dev-02, then
scripts/verify faston compute01. Current dev contains D243 report-only size reporting from5418f9357; I will include both shipping-target deltas with the gate result. Logs will be under each VM's.cache/codex-scratch/j49-mesh-relay-220/. Issue remains open.J49 post-rebase validation on
960a2a364d90face69abcd0d55025e612449d3f7:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/feature-on-current.log.cuasudo/build-slot rerun passed. Direct and relayed 32 MiB streams, the 384 MiB relay-kill/failover stream, and the 16 MiB rebound stream all had matching hashes; the old mapping retired. It ended withstill_loaded: [], empty link/netns/module/sysctl diffs, andunchanged: true. Receipt:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/root-proof-current.log. An earlier unprivileged invocation failed to clean modules; I removed the four modules it alone loaded and verified they were absent before the successful proof.Next:
scripts/verify faston compute01; current dev reports static-size deltas instead of applying D243 size caps.J49
scripts/verify fastfinished on compute01 at960a2a364d90face69abcd0d55025e612449d3f7; the full log is/home/claude/.cache/codex-scratch/j49-mesh-relay-220/verify-fast-current.log.All Rust rows passed except the workspace test row (exit 101). The failing test is
infraternal/tests/status_restart.rs::service_states_are_truthful_in_text_and_json; the system service query timed out after 0.62 s and returnedsystem/server/activating. Clippy, fixtures, cargo-deny, architecture, and both static builds passed. D243 deltas are x86_64 +6,368 B and aarch64 +6,256 B, report-only. I am rerunning this one test in isolation and against unmodifiedorigin/devto determine whether the failure is branch-specific.The isolated
service_states_are_truthful_in_text_and_jsonrerun also fails on J49 head960a2a364in 0.23 s withsystem service; service manager query timed out. No other build/gate process is running from J49. I am using the same compute01 worktree, temporarily detached atorigin/dev, to run the exact test throughbuild-slot, then restoringmesh-relay-220; this will establish whether the failure is pre-existing without leaving another worktree or target directory.Baseline comparison complete: on the same compute01 machine and through build-slot,
origin/dev37bbda45554f6e603a6856fc23319e17d8a1b14fpassedservice_states_are_truthful_in_text_and_json1/1 in 1.35 s. The temporary detached checkout restoredmesh-relay-220to960a2a364d90face69abcd0d55025e612449d3f7with a clean worktree.This does not reproduce the J49 full-gate failure on dev. I am rerunning
scripts/verify fastunder build-slot at the same head to check whether the earlier 0.62 s system-service timeout was load-sensitive.J49 round-2 implementation finished. Branch
mesh-relay-220is rebased onorigin/dev37bbda45554f6e603a6856fc23319e17d8a1b14f, clean, and pushed with lease at960a2a364d90face69abcd0d55025e612449d3f7. P1 and P2 are separate commits:964607892and960a2a364.P1 — stale same-second handshake suppressed relay fallback
direct_failednow compares WireGuard handshake seconds and nanoseconds to the endpoint-installSystemTime. Equal or older timestamps allow relay fallback; a later handshake in the same second proves the direct path. J43'sa_same_second_handshake_cannot_prove_it_followed_the_endpoint_updatefailed before the fix and passes now, along with the fresh-same-second and exact-equality cases. The selector-path search found no other endpoint-freshness comparison using seconds-only ordering;start_handshakeonly checks whether a cryptographic session exists. The contract records the ordering rule.P2 — aggregate relay DATAGRAM memory bound
The relay now has a 64 MiB global forwarding budget and a 256 KiB per-pair cap, charged in 1 KiB units with 128 bytes per-frame allowance. Forwarding retains the existing shared
Bytesbuffer, and both reservations stay with it through Quinn's send queue. DATAGRAMs are dropped and counted when a budget or send queue is full. The relay receive queue is 8 KiB per connection and the send ceiling is 128 KiB. At 4,096 hosts, configured receive queues plus the global forwarding budget account for at most 96 MiB of DATAGRAM buffering, excluding fixed connection, control, and link-table state.contracts/mesh-relay.mddocuments these limits and the drop behavior.Perf VM
infraternal-perf-01-backup01, usingperf-run --rootin an isolated network namespace, same abuse workload: 256 pairs / 512 hosts / 131,072 attempts. Both quiet checks passed.tcis unavailable, so loopback was unshaped.Feature-on checks on dev-02 through
build-slot/nix develop: mesh relay-off 64 passed / 14 ignored; mesh relay-on 73 passed / 14 ignored; relay unit tests 12/12; QUIC loopback tests 4/4. This includes the J43 regression and shared-budget regressions.The root namespace proof ran through
cuasudo andbuild-sloton dev-02. Direct and relayed 32 MiB streams, the 384 MiB relay-failover stream, and the 16 MiB rebound stream had matching SHA-256 hashes; the old mapping retired. Relay-kill maximum gap was 3,132.8 ms and recovery after relay restart was 551.4 ms. The proof ended withstill_loaded: [], empty link/netns/module/sysctl diffs,unchanged: true, and exit 0. Receipt:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/root-proof-current.log.The first full fast-gate attempt had a system-service query timeout in
service_states_are_truthful_in_text_and_json; the same test passed 1/1 on unmodifiedorigin/dev, and it passed in this complete rerun.scripts/verify fastran on compute01 throughbuild-slot, exit 0. Currentorigin/devcontains5418f9357; static builds are report-only. D243 deltas are x86_64 +6,368 B and aarch64 +6,256 B.Verbatim gate summary:
Full gate log:
/home/claude/.cache/codex-scratch/j49-mesh-relay-220/verify-fast-r2.log. Perf receipts:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/before.logandafter.log. Feature log:/home/claude/.cache/codex-scratch/j49-mesh-relay-220/feature-on-current.log. All J49 target directories were removed after validation. Issue remains open.J65 round-2 review starting on
review-j65-220, with review baseorigin/dev37bbda45554f6e603a6856fc23319e17d8a1b14fand target head960a2a364d90face69abcd0d55025e612449d3f7.Plan (estimated 30 minutes): (1) verify J43's freshness and aggregate-queue findings against the fix diff and tests; (2) trace relay admission, forwarding, resource bounds, and failover against D313, D316, and the contract under hostile inputs; (3) post evidence-backed P0–P3 findings or APPROVE here. I will run tests only if needed to prove a concrete finding.
Verdict: HOLD for fixes. Review target
mesh-relay-220at960a2a364d90face69abcd0d55025e612449d3f7, based onorigin/dev37bbda45554f6e603a6856fc23319e17d8a1b14f.The two J43 findings are fixed:
direct_failedcompares the validated kernel seconds+nanos timestamp with the endpoint-install time, and the stale-same-second, fresh-same-second, and equality cases are covered. The forwarding path reserves both global and per-pair units, keeps reservations in the sharedBytesowner, and counts application-level invalid, unlinked, budget-refused, and send-queue-refused datagrams.P1 — Unbounded pre-authentication handshakes.
crates/plugins/relay/src/relay.rs:583-615spawns oneservetask for every QuinnIncoming; thehostslimit is checked only at:341, after the QUIC/TLS handshake, certificate admission and control hello. The 5-second handshake timeout bounds each task's lifetime, not the number alive at once. An unauthenticated sender can hold arbitrary incoming handshake tasks and Quinn state concurrently, bypassing both the host cap and admission callback. Add a bounded in-flight handshake limit before spawning/accepting each task.P2 — One admitted host can monopolize the global forwarding budget.
crates/plugins/relay/src/relay.rs:38-41,57-58,171-184,388-414has global and per-pair budgets but no per-host aggregate budget. A host may hold 1,024 requests; with 256 mutually requested, backpressured pairs, it can reserve 256 × 256 KiB = the whole 64 MiB global budget. Other pairs then fail the same global reservation and drop until the attacker's queues drain. The memory cap holds, but one peer can deny relay service to unrelated pairs. Add per-host aggregate quota or fair global admission.P3 — Captured probes can be replayed as a one-for-one reflector.
crates/plugins/relay/src/probe.rs:80-96verifies a request HMAC and immediately creates a reply without tracking transaction IDs;crates/plugins/relay/src/relay.rs:418-432,465-472sends that reply to the packet's source address. A party that captures a valid live probe can replay it with a spoofed source and make the relay reflect an 84-byte reply. It is not an amplifier (request and reply are equal-sized), and the target node will not accept it as a mapping, but it is still reflection. Add bounded replay suppression or equivalent source/replay validation.No P0 found. The relay forwards inner WireGuard ciphertext; TLS identity, admission, mutual link requests, and the sending-connection check prevent a third peer from hijacking a link. Slice-1 ordered relay retry and no-failback behavior match the contract; D197 quality selection and UDP-blocked TLS/WebSocket support are explicitly deferred.
Review-only: no tests or
scripts/verify fastrun; the findings follow directly from these code paths, and the review scope permits tests only when needed to prove a finding. Target head reviewed: `960a2a364d. No code or test files changed.J65 round-2 fix work starting on
mesh-relay-220at960a2a364d90face69abcd0d55025e612449d3f7, based on currentorigin/dev37bbda45554f6e603a6856fc23319e17d8a1b14f.Plan (estimated 110 minutes):
scripts/verify iterateon dev-02 between commits. Measure the handshake flood before and after on the perf VM viaperf-run --root, building on dev-02. Run finalscripts/verify faston compute01, post the output and measurements, and remove J65 target directories.Scope stays within the relay/probe code, directly related tests, and the smallest measurement support needed. No repository-wide infrastructure changes.
P1 — pre-auth handshake task bound
Committed and pushed
ac86fd5cc.acceptnow reserves one of 256 global and eight per-source-IP slots (defaults) before spawningserve. IPv4-mapped IPv6 sources are canonicalized, excess Incoming connections are refused before task creation, and the permit is released after the authenticated host andIFR1hello are accepted. The existing five-second TLS and hello deadlines remain in force.Evidence:
relay_bounds_stalled_handshakes_from_one_source_ipfailed before the fix withleft: 9, right: 8; after the fix the relay crate passed 13 unit tests and 5 loopback tests, andcargo fmt --all -- --checkpassed.The dev-02
scripts/verify iterateattempt was stopped before completion when shared disk fell below the 25 GiB floor (4.2 GiB free at stop). Its log is~/.cache/codex-scratch/j65-mesh-relay-220/p1-iterate.log; the J65 target was removed afterward. No iterate-gate pass is claimed. Continuing with P2; finalscripts/verify fastremains assigned to compute01.P2 — per-host aggregate queue share
Committed and pushed
5b8c832ed. A forwarded DATAGRAM now reserves the global, source-host and per-pair budgets together, retaining those reservations in the sharedBytesowner. The source-host cap is 4 MiB across its pairs; it is reused across reconnects while older queued buffers still hold reservations. This leaves unrelated source hosts able to use the remaining 60 MiB of the 64 MiB global budget.Evidence: the 256-pair regression failed before the fix because its unrelated pair could not reserve a global unit. After the fix, relay tests passed 15 unit and 5 loopback tests, including the 256-pair case and reconnect-budget case;
cargo fmt --all -- --checkand relay all-target clippy with-D warningspassed.P1 commit hash correction: the clippy follow-up was folded into the same atomic commit and force-pushed with a lease. The current P1 commit is
1ccdd066ac74ad1d12b19551032cf92946b8e9e0(the earlierac86fd5cchash in my prior comment was replaced).J65 resumed from mesh-relay-220 at
5b8c832ed, based exactly on origin/dev37bbda455. The current HOLD findings are being checked against the pushed P1/P2 commits and local P3 patch. dev-02 fell below its 25 GiB build floor (5 GiB free); no J65 build is running there. I removed only the stale J65 target directory, which raised free space to 11 GiB. I am preparing the P3 red/green reproduction and flood harness while waiting for the shared disk floor to recover. No verification pass is claimed yet.J65 rebase update:
mesh-relay-220is now based on currentorigin/deva26615c50e76ed86f36d25aa094283cf840d5320and pushed atb9c7c33ecbac72c10d08946b9c5376af528113fc. The three finding commits are P17a70bccd7(pre-auth global/source-IP caps; includes the 1,024-Initial flood driver), P289619b35c(per-host queue share), and P3b9c7c33ec(per-session probe source binding). These hashes supersede earlier J65 hash comments.P3 RED evidence on dev-02: with only the regression test added to the pre-fix
probe.rs,a_captured_request_cannot_be_replayed_to_a_different_sourcefailed at the intended assertion because the relay returned a reply to the spoofed source (RED_EXIT=101). The fix now passes that test. I am rerunning relay and mesh feature-on/off checks on the rebased head; no final gate result is claimed yet.J65 rebased feature-check evidence from
a26615c50headb9c7c33ec: the relay crate passed 17 unit tests and 5 QUIC loopback tests; all-target clippy with-D warningspassed. Mesh agent-only tests passed (38 passed, 13 ignored in the unit binary), and mesh relay-on tests passed (47 passed, 13 ignored in the unit binary); the remaining package integration/doc test binaries in both runs also completed with zero failures.cargo fmt --all -- --checkpassed. I am building the relay fixture for the final root namespace proof now.J65 continuation from the pushed WIP: branch head remains
b9c7c33ec, clean and based on current origin/deva26615c50. The three findings remain in three separate commits; the review comment's RED evidence and the feature-on/off checks are recorded above.The dev-02 root namespace proof is still waiting in build-slot. Free disk has fallen to 24 GiB, below its 25 GiB start floor, so the proof has not started. Its queue log is ~/.cache/codex-scratch/j65-mesh-relay-220/root-proof.log.
For the requested P1 measurement, compute01 now has a J65 worktree. The release build of the committed handshake-flood driver at pre-cap parent
850064878is queued through build-slot; log: ~/.cache/codex-scratch/j65-mesh-relay-220/build-before.log. Next: run that baseline and final-head binaries through perf-run --root, complete the root proof, run scripts/verify fast on compute01, and remove J65 target directories.P1 flood baseline completed on
infraternal-perf-01-backup01usingperf-run --root(cpus 1–3, 6 GiB cap, quiet-check receipts retained). The pre-cap relay at850064878received 1,024 attempted Initials from 128 loopback source IPs, eight per source, while the clients blackholed server replies and withheld the control hello. Idle RSS/HWM was 8,256 KiB; flood RSS/HWM was 28,064 KiB; peak-over-idle was 19,808 KiB; sample time was 2,003 ms. Perf log:/home/claude/.cache/codex-scratch/j65-mesh-relay-220/perf-before.log.The final-head release build is queued on compute01 through build-slot. The dev-02 proof is queued through the CUA-sudo/real-UID-preserving wrapper; dev-02 free disk is currently 24 GiB, below the 25 GiB floor. No post-fix flood, root proof, or final gate result is claimed yet.
J65 continuation at
b9c7c33econmesh-relay-220, based on currentorigin/deva26615c50; the three fix commits remain pushed separately. RED evidence, feature-on/off checks, and the before/after handshake-flood measurements are recorded above.The fresh
scripts/verify fastrun is still active on compute01; log:/home/claude/.cache/codex-scratch/j65-mesh-relay-220/verify-fast.log. The root namespace proof is queued on dev-02. It will run with CUA sudo while a claude-ownedbuild-slotreservation is held; proof log and exit receipt:/home/claude/.cache/codex-scratch/j65-mesh-relay-220/root-proof.logandroot-proof.exit. I will post the verbatim gate result and namespace evidence when both complete.Namespace proof
The root namespace proof passed on dev-02/aarch64 at the current branch head, with CUA sudo under a claude-owned
build-slotreservation. Direct and relayed 32 MiB streams, the 384 MiB relay-kill/failover stream, and the 16 MiB rebound stream all had matching SHA-256 hashes. Relay-kill maximum stream gap was 3,107.7 ms; recovery after relay restart was 4,086.8 ms. The old mapping retired. The proof ended withstill_loaded: [], empty link/netns/module/sysctl diffs,unchanged: true, and exit 0. Full receipt:/home/claude/.cache/codex-scratch/j65-mesh-relay-220/root-proof.logon dev-02.The final
scripts/verify fastis still active on compute01. Log:/home/claude/.cache/codex-scratch/j65-mesh-relay-220/verify-fast.log. I will post its verbatim output after completion.origin/devadvanced toafbe1c5feduring the queued verification. I rebasedmesh-relay-220onto it and pushed with an explicit lease at7bbef6359. The three J65 fixes remain separate commits: pre-auth capsebd2ec6f3, per-host queue share334ce6700, and probe replay binding7bbef6359.The previous full gate and namespace proof passed at
b9c7c33ec; that result is superseded by this rebase. I am refreshing the feature-on checks and fixture on dev-02, the root namespace proof, and the finalscripts/verify faston compute01. I will report the new receipts at the rebased head.J65 rebase update:
origin/devadvanced to65d986191, so I rebasedmesh-relay-220and pushed with lease at051fdd2df. The three findings remain in separate commits: P1dd5b9d9a4, P21d8f0bb20, P3051fdd2df.The compute01 gate queued for the earlier
afbe1c5febase was stopped before entering a slot (exit 143; no gate work ran). I am refreshing relay/mesh feature-on checks and the root namespace proof on dev-02. The replacement compute01 gate is queued withCARGO_BUILD_JOBS=4and the short test-state-root override. Logs will usefeature-checks-final.*,root-proof-final.*, andverify-fast-final.*under~/.cache/codex-scratch/j65-mesh-relay-220/.J65 validation on current head
051fdd2dfis progressing: the relay and mesh feature checks, clippy, and relay fixture build finished withFEATURE_CHECKS_FINAL_EXIT=0on dev-02. The refreshed CUA-sudo namespace proof also passed under a claude-owned build-slot reservation. Its direct and relayed 32 MiB streams, 384 MiB relay-failover stream, and 16 MiB rebound stream all matched SHA-256; the old mapping retired, the relay-failover maximum gap was 3,099.1 ms, recovery after restart was 4,910.8 ms,still_loadedwas empty, host link/netns/module/sysctl diffs were empty, andunchanged: true. Full receipt:/home/claude/.cache/codex-scratch/j65-mesh-relay-220/root-proof-final.logon dev-02.I removed the completed dev-02 J65 target after copying the fixture. The final
scripts/verify fastremains queued on compute01 withCARGO_BUILD_JOBS=4and the short state-root override. Log and exit receipt:/home/claude/.cache/codex-scratch/j65-mesh-relay-220/verify-fast-final.logand.exit. No final gate result claimed yet.Gate follow-up on
051fdd2df(base65d986191): the firstscripts/verify fastexited 1 becauseservice_states_are_truthful_in_text_and_jsontimed out querying systemd (agentandhostreportedservice manager query timed out). The same test passed 1/1 on an unmodified worktree at currentorigin/dev65d986191. The failed gate's other rows passed, including report-only static builds: x86_64 21,655,056 bytes (+211,232); aarch64 19,794,344 bytes (+165,104).I am rerunning the full gate on
051fdd2dfwithCARGO_BUILD_JOBS=4andINFRATERAL_AGENT_TEST_STATE_ROOT=/tmp/j65-verify-state. The rerun log and receipt are/home/claude/.cache/codex-scratch/j65-mesh-relay-220/verify-fast-rerun.logand.exit; the first attempt is preserved asverify-fast-final.log/.exit.J65 round-2 fixes are complete on
mesh-relay-220at051fdd2df5395ec6c1c52ab73f74bcdae90041cb, rebased on currentorigin/dev65d9861914e8cd4a734d533d64ed1f4ea5c34119and pushed with lease. The three findings remain separate commits:dd5b9d9a432c72967a7bff3cc71a1c136cccb5d: reserve up to 256 global and eight canonical source-IP handshake slots before spawning; refuse excess Incoming connections. The five-second TLS and control-hello deadlines bound each admitted handshake.relay_bounds_stalled_handshakes_from_one_source_ipfailed before the fix (left: 9, right: 8).1d8f0bb20a57a30aa4ad5b20820ec681852e0965: charge forwarded buffers to a 4 MiB source-host share as well as the 64 MiB global and 256 KiB per-pair budgets. The 256-backpressured-pair test showed an unrelated sender could not reserve before the host quota. DATAGRAMs drop under pressure.051fdd2df5395ec6c1c52ab73f74bcdae90041cb: bind each probe transaction to its first observed source in a bounded 64-entry per-session window; same-source retries work, source changes are refused. The captured-probe replay regression returned a reply to the spoofed source before the fix.contracts/mesh-relay.mdrecords the limits and probe binding. After rebasing, dev-02 feature checks passed: relay 17 unit + 5 QUIC loopback tests, mesh agent 38 passed / 13 ignored, mesh relay 47 passed / 13 ignored, clippy clean, fixture built (FEATURE_CHECKS_FINAL_EXIT=0).Handshake flood measurement
Measured on
infraternal-perf-01-backup01withperf-run --root; 1,024 attempted Initials from 128 source IPs, eight per source, while replies were blackholed and the control hello withheld. The measured relay code is byte-identical to the final head for the relay paths.Both runs exited 0 with zero steal jiffies.
Root namespace proof
The refreshed CUA-sudo proof passed on dev-02 under a claude-owned build-slot reservation. Direct and relayed 32 MiB streams, the 384 MiB relay-failover stream, and the 16 MiB rebound stream matched SHA-256; the old mapping retired. Maximum failover gap was 3,099.1 ms, relay restart recovery was 4,910.8 ms.
still_loaded: [], link/netns/module/sysctl diffs were empty, andunchanged: true. Receipt:/home/claude/.cache/codex-scratch/j65-mesh-relay-220/root-proof-final.log.Final
scripts/verify fastThe first run hit a systemd query timeout in
service_states_are_truthful_in_text_and_json; that test passed 1/1 on unmodified currentorigin/dev65d986191. The complete rerun on this head exited 0. Static builds are report-only under the amended D243 policy; deltas are +211,232 bytes x86_64 and +165,104 bytes aarch64.Verbatim gate summary:
Gate exit receipt:
0. The completed J65 target directories were removed from dev-02 and compute01. Issue remains open.J76 round 3 starting on branch mesh-relay-220 at
051fdd2df5, against current origin/dev0067e12554. Plan (estimated 45 minutes): verify the three round-2 fixes and tests; trace the relay-restart recovery regression and unchanged failover gap against the proof history; check ciphertext-only forwarding, amplification limits, per-host bounds, failover and rebound byte equality; post numbered findings or APPROVE. Review-only; I will run a test only if needed to confirm a concrete finding.Verdict: HOLD for the P2 restart-recovery defect and the P3 probe-replay gap. No P0 or P1 found.
Target:
mesh-relay-220at051fdd2df5395ec6c1c52ab73f74bcdae90041cb, based onorigin/dev0067e1255407f0f3ed0f1d6c5e5caf51667e00a(merge base65d9861914e8cd4a734d533d64ed1f4ea5c34119).P2 — A relay restart can leave every peer waiting almost five seconds to reconnect.
crates/plugins/mesh/src/relay_path.rs:50-52,515-531caps retry backoff at five seconds and sleeps the current backoff after each failed attempt; it resets only after a successful connection. The namespace proof restarted relay 1 after it had already been unavailable during failover. Its recovery result rose from 551.4 ms in J49 to 4,910.8 ms in J65, consistent with the remaining maximum retry sleep. The newly added relay admission cap and timeout do not explain this proof: it has only nodes A and B using relays, behind distinct public NAT addresses, so neither reaches the eight-per-IP cap (scripts/mesh-relay-proof.py:40-50,230-300); the client sendsIFR1immediately after opening its control stream (crates/plugins/relay/src/client.rs:31-48). The three-second relay-kill gap remains about 3.1 s, consistent with the unchanged QUIC idle timeout. Under this review's restart-recovery requirement, the five-second retry ceiling is a defect: a relay restart should not leave all pairs unavailable for about five seconds.P3 — Probe source binding expires when its transaction is evicted.
crates/plugins/relay/src/probe.rs:35-54drops the oldest source binding when the 64-entry window fills. The test at:265-280then explicitly rebinds that evicted transaction to a different source. A captured request can therefore again produce a one-for-one 84-byte reflection after 64 newer transactions. Replies remain the same size as requests (:237), so this is not amplification, but the transaction is not bound to its first source for its lifetime as requested.P3 — The pre-auth timeout has no regression test. The two five-second timeout paths are present at
crates/plugins/relay/src/relay.rs:716-730, butcrates/plugins/relay/tests/relay.rs:153-173closes its eight stalled clients itself before the deadline; the limiter unit test atrelay.rs:959-981manually drops permits. No test exercises TLS or hello timeout expiry and permit release. The concurrent/global and per-IP caps are tested; the separate 4 MiB per-host share and reconnect reuse are covered byrelay.rs:889-950; probe source binding is covered while its transaction remains in the window.Other requested checks: the relay preserves the existing DATAGRAM bytes while checking only the link ID (
crates/plugins/relay/src/relay.rs:514-528;crates/plugins/mesh/src/relay_path.rs:362-405). Probe requests require the authenticated connection's HMAC key and replies are fixed-size. The recorded J65 namespace proof has byte-matching direct and relayed 32 MiB streams, the 384 MiB relay-failover stream, and the 16 MiB rebound stream;still_loadedwas empty and host link/netns/module/sysctl state was unchanged. The per-host forwarding cap is 4 MiB and its 256-pair regression passes in the recorded relay checks.Review-only: no code changed and no tests or
scripts/verify fastwere run; the existing J65 feature-check, namespace-proof and gate results are recorded above on #220. Review head:051fdd2df5395ec6c1c52ab73f74bcdae90041cb.J76 fixes starting on mesh-relay-220 at
051fdd2df5; rebasing onto current origin/dev0067e12554. Plan (estimated 90 minutes): add and watch each requested regression fail; implement relay retry backoff, probe time-window binding, and stalled-handshake timeout/permit-release coverage as three separate commits; rerun the namespace proof; run one complete scripts/verify fast gate; push the final branch head. Keep changes limited to those code paths, tests, and the existing proof.J76 progress at
45d0f90c0: the probe-window finding is committed and pushed separately. The restart-recovery RED guard is in scripts/mesh-relay-proof.py. The CUA-launched build-slot command is queued on dev-02 (disk now above the 25 GiB floor); all compute01 slots are occupied. Earlier wrappers using the wrong identity were stopped. Waiting for the before-fix proof result before changing retry behavior.J76 P2 RED evidence on dev-02/aarch64: the namespace proof failed the new 1.5 s guard at 4,964.9 ms, matching the reported five-second backoff. A rootless run left bridge/llc/stp/veth loaded; I verified they had no active references and removed them through CUA sudo, restoring the host. The fix now retries immediately after a healthy relay disconnect, caps retries at 1 s for 30 s, then permits longer delays after continued failures. The updated relay fixture built successfully (BUILD_EXIT=0); the post-fix root proof and timeout test remain queued behind build slots.
J76 P2 retry repair is pushed on
mesh-relay-220at25a560599, rebased ontoorigin/devad2b2c046.The namespace regression failed at 4,964.9 ms on the old backoff. A one-second recent-health sleep cap alone still measured 2,292.6 ms. Temporary client timing showed connect attempts timing out at 3.0 s on QUIC's idle timeout before the 1 s retry sleep. The fix keeps the immediate first retry, then uses a 750 ms connect deadline and 250 ms retry cap for 60 seconds after a healthy connection ends; initial and sustained-failure behavior retains the 5 s deadline and exponential backoff.
The final CUA-sudo root namespace proof passed on dev-02 with relay 1 down 54,023.5 ms: restart-to-ready 3.6 ms, ready-to-links 105.4 ms, recovery 588.4 ms. Direct, relayed, failover and rebound streams matched SHA-256.
still_loadedwas empty; link/netns/module/sysctl diffs were empty andunchanged: true. Receipt:/tmp/j76-mesh-relay-220/proof-slot-60/proof-60.logon dev-02.Feature-on relay and mesh regression suites passed on the rebased tree. Final
scripts/verify fastis running next.J76 final review fixes are pushed on
mesh-relay-220.Base:
origin/devd1ec515e44dabccb640801c4a1ef159b32e004feHead:
9ae031bc8596be8c9e8d3b4d16b0142ebcb2de5dFindings, one commit each:
9ae031bc8): recently healthy relay retries use a 750 ms connect deadline and 250 ms backoff cap for 60 seconds; initial and sustained failure backoff remains longer. Root namespace proof recovered in 588.4 ms after a 54,023.5 ms relay outage; restart-to-ready was 3.6 ms, ready-to-links 105.4 ms, ping loss 0%, and host state was unchanged.e1630d06b): transaction issue time is checked against a 30 second window and an evicted transaction advances a retired-time floor, preventing an old ID from rebinding after 64 newer IDs.89c42de08): regression coverage confirms a stalled hello is closed at the deadline and releases its permit.The interrupted earlier run’s SIGKILLed restart test passed on unmodified current dev: 1 passed, 0 failed, in 10.72 s.
scripts/verify faston compute01, exit 0. Verbatim Rust gate summary:Static size output (report-only):
Namespace proof receipt:
/tmp/j76-mesh-relay-220/proof-slot-60/proof-60.logon dev-02. Full gate log:/home/claude/.cache/codex-scratch/j76-mesh-relay-220/verify-fast-final-2.logon compute01.