Skip to content

Prevent partial chain state from escaping host recovery - #14

Closed
lr00rl wants to merge 25 commits into
integrationfrom
feat/root-design18-e3-node-agent
Closed

Prevent partial chain state from escaping host recovery#14
lr00rl wants to merge 25 commits into
integrationfrom
feat/root-design18-e3-node-agent

Conversation

@lr00rl

@lr00rl lr00rl commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Outcome

  • apply E3 fragment and sidecar through one crash-recoverable host transaction
  • gate hello, metrics, inventory, polling, and durable results on exact-pair recovery
  • preserve one stable E3 result through the linechain journal to taskoutbox handoff
  • wire the private installer transaction root and alpha compatibility metadata
  • add failure-focused tests and a mandatory real sing-box client -> B -> A -> origin harness

Verification

  • go test ./... -count=1
  • go test -race -cover ./... -count=1
  • go vet ./...
  • sh scripts/test-install-integrity.sh
  • LATTICE_SINGBOX_E2E_BIN=/absolute/path/to/sing-box sh scripts/test-linechain-e2e.sh (sing-box 1.13.18)
  • git diff --check
  • secret canary scan: clean

Boundaries

  • target: integration
  • no dependencies added
  • no signing, tag, release, deployment, publishing, or fleet mutation performed
  • server-to-agent payload integration remains an exact-contract cross-lane check before merge

lr00rl and others added 25 commits August 12, 2026 17:14
* fix: raise the task file-size cap from 8 MiB to 256 MiB

The 8 MiB RLIMIT_FSIZE capped every file a task script could write —
including the agent's own release binary during a self-update. The
2026-08-12 fleet upgrade died to SIGXFSZ (exit 153, 'File size limit
exceeded') on 18 of 20 attempted nodes. The server-side update script now
lifts the inherited cap itself (the only fix that reaches agents already
deployed); this raise is the belt-and-braces for non-update tasks and for
future agents once their update has run. 256 MiB still bounds runaway
writes.

* fix: bump grpc to v1.82.1 (GO-2026-6061)

govulncheck flags the sing-box stats gRPC client path under the pinned
v1.72.2. Unrelated to the rlimit change; the vuln DB entry postdates the
last release. protobuf rides to v1.36.11. Full suite green.
The E3 transaction path depends on the task execution and 256 MiB output baseline already present on main, while the approved feature branch must retain the exact integration ancestry. Merge that prerequisite before adding E3 behavior.

Constraint: Feature work starts at exact integration c783354 and requires main d2666e9
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: ancestry and exact prerequisite OID verified
Not-tested: E3 behavior is introduced by the following commit
Apply the E3 fragment and source-side declaration through one private, crash-recoverable transaction manager. Recovery now precedes observable agent traffic, transfers one stable terminal result into the durable outbox, and retains generic task behavior. Installer and compatibility wiring make the private transaction root and alpha contract explicit, while the real sing-box harness proves client-to-source-to-target traffic.

Constraint: E3 uses the existing opaque shell task channel and adds no dependency
Constraint: Host state must be exactly the old pair or desired pair before result publication
Rejected: Shell traps alone | cannot recover helper SIGKILL or power loss
Rejected: Generic interrupted-task synthesis | could publish an unknown result before host recovery
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep linechain journal cleanup after confirmed outbox completion and before acknowledged outbox removal
Tested: go test ./...; go test -race -cover ./...; go vet ./...; installer integrity; real sing-box 1.13.18 client-B-A-origin E2E
Not-tested: Full server-to-agent E3 payload integration awaits the server lane exact contract
The initial E3 lane left authority and crash-boundary gaps around lock files, runtime layout, result retention, and artifact binding. This follow-up refuses hostile lock state, resolves layout locally, retains only E3 results during cleanup retries, binds combined digests to raw artifact bytes, and makes transaction roots durable and private.

Constraint: Server documents cannot choose arbitrary host paths
Constraint: Generic task result behavior must remain unchanged
Rejected: Repairing hostile lock modes in place | could mutate another owner’s state
Confidence: high
Scope-risk: moderate
Directive: Keep cleanup retryable without retaining unrelated generic results
Tested: full normal/race-cover/vet/gofmt/diff/install gates
Not-tested: Full live server payload replay remains cross-lane integration work
The follow-up touched existing tests and runtime layout discovery while closing the crash and authority gaps. Normalize those files so CI applies the same formatting gate as local verification.

Constraint: CI rejects any gofmt drift
Confidence: high
Scope-risk: narrow
Tested: gofmt and targeted Go tests
Not-tested: none
Generic durable shell tasks must not be classified as replayable linechain operations merely because their script mentions a helper string. Require the server-authored E3 marker, helper invocation, and transaction-root binding together.

Constraint: Generic result endpoints are not replay-safe
Confidence: high
Scope-risk: narrow
Directive: Server task rendering must emit lattice-linechain-e3-v1 exactly
Tested: go test, race-cover, vet, gofmt, diff
Not-tested: full live server payload replay
Use explicit leased protocol metadata for live classification and derive artifact paths from the agent-resolved sing-box layout while retaining a narrow legacy recovery marker.\n\nConstraint: Existing pending journals do not carry leased protocol metadata\nRejected: Trust server-supplied absolute paths | permits host redirection\nConfidence: medium\nScope-risk: moderate\nDirective: Remove legacy marker fallback once all persisted outbox entries carry durable_protocol\nTested: go test ./internal/linechain ./cmd/lattice-agent\nNot-tested: authentic external sing-box E2E harness
Require explicit linechain-e3-v1 metadata for durable E3 execution and remove script-marker classification.\n\nConstraint: pending outbox records do not yet persist protocol metadata\nRejected: Parse script comments | spoofable and conflates generic delivery\nConfidence: medium\nScope-risk: moderate\nTested: gofmt; targeted go test (legacy cleanup regression remains)\nNot-tested: full contract matrix and real-binary E2E
Add an explicit protocol field and protocol-aware Begin seam so recovery can classify pending deliveries without parsing scripts.\n\nConstraint: existing Begin callers and persisted entries must remain readable\nRejected: infer protocol from task script | spoofable\nConfidence: medium\nScope-risk: narrow\nTested: go test ./internal/taskoutbox\nNot-tested: agent integration wiring and real E2E
Recovery failure now gates readiness and polling without flushing pending results, preventing arbitrary durable entries from crossing an unresolved linechain authority boundary.\n\nConstraint: recovery must precede every observable delivery\nRejected: flush retained results during blocked recovery | journal/outbox identity may be unresolved\nConfidence: high\nScope-risk: moderate\nTested: focused linechain tests (remaining cleanup retention assertion fails)\nNot-tested: full matrix and real E2E
Use the protocol-aware outbox seam when available while preserving compatibility with existing outbox implementations.\n\nConstraint: mocks and older stores expose only Begin(task)\nRejected: change interface abruptly | breaks generic and NetGuard implementations\nConfidence: medium\nScope-risk: narrow\nTested: focused linechain test (cleanup retention remains red)\nNot-tested: full matrix and real E2E
Treat protocol and durability as one leased-task contract, admit generic, NetGuard, and E3 pairs only, and bind protocol to duplicate journal identity. E3 tasks are rejected before journaling when linechain is unavailable.\n\nConstraint: protocol metadata is server-authored and security-sensitive\nRejected: accept durable=true with missing protocol | ambiguous execution authority\nConfidence: high\nScope-risk: moderate\nTested: internal/taskoutbox passes; existing agent tests require fixture updates for explicit protocol pairs\nNot-tested: full contract matrix and real E2E
Accept the v2 document envelope while preserving v1 journal compatibility, and require the agent-owned fragment basename before local path derivation.\n\nConstraint: existing journals and tests remain version 1\nRejected: trust server absolute paths in v2 | host path redirection\nConfidence: medium\nScope-risk: narrow\nTested: go test ./internal/linechain\nNot-tested: semantic sidecar merge and real E2E
Perform raw decode-time rejection of legacy absolute path fields before deriving agent-owned artifact locations.\n\nConstraint: v1 documents and journals retain compatibility\nRejected: validate after normalization | derived local paths obscure hostile input\nConfidence: high\nScope-risk: narrow\nTested: go test ./internal/linechain\nNot-tested: semantic sidecar overlay and real E2E
Require the v2 fragment basename to match the exact lowercase hexadecimal namespace contract.\n\nConstraint: server must bind deterministic artifact identity\nRejected: broad prefix and extension checks | permits ambiguous names\nConfidence: high\nScope-risk: narrow\nTested: go test ./internal/linechain\nNot-tested: sidecar overlay and real E2E
Replace the file-write smoke test with a loopback-only sing-box 1.13.x harness that observes the B-to-A hop, drives basename-only v2 Manager documents through create/resync/remove, validates runtime discovery, and exercises process-group crash recovery while the supervisor remains alive. Keep the binary external and mandatory in the script lane.

Constraint: E2E owns only the agent test and invocation script; Manager, main loop, and outbox integration remain worker-2 owned
Constraint: The official sing-box binary is a local test tool, never a Go or repository dependency
Rejected: Stub restart/check commands | they cannot prove that B loads the published fragment
Rejected: Starting B pre-chained | it cannot prove the apply operation changed live routing
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep the v2 fixture basename-only and preserve real process restart plus observer assertions
Tested: go test ./cmd/lattice-agent -run '^TestLinechainE2E(Apply|Restart|Active)Helper$' -count=1
Not-tested: Full real-binary lane pending integration with worker-2 final v2 Manager head
Update the cleanup-failure regression to exercise the explicit leased E3 protocol contract rather than obsolete durable-result-only payloads.\n\nConstraint: generic and NetGuard deliveries must remain distinct\nRejected: infer E3 from script marker | spoofable\nConfidence: high\nScope-risk: narrow\nTested: focused test remains red, exposing outbox completion protocol propagation gap\nNot-tested: full matrix and real E2E
The integration regression must model a node that resolved its local sing-box layout; otherwise the protocol guard correctly rejects the lease before exercising retention.\n\nConstraint: E3 execution is forbidden when readiness is false\nRejected: weaken production guard | would permit unsafe host execution\nConfidence: high\nScope-risk: narrow\nTested: go test ./cmd/lattice-agent -run TestRunTasksLinechainCompletesHandoffWithoutReplay -count=1\nNot-tested: full matrix and real E2E
Replace the file-write smoke test with a loopback-only sing-box 1.13.x harness that observes the B-to-A hop, drives basename-only v2 Manager documents through create/resync/remove, validates runtime discovery, and exercises process-group crash recovery while the supervisor remains alive. Keep the binary external and mandatory in the script lane.

Constraint: E2E owns only the agent test and invocation script; Manager, main loop, and outbox integration remain worker-2 owned
Constraint: The official sing-box binary is a local test tool, never a Go or repository dependency
Rejected: Stub restart/check commands | they cannot prove that B loads the published fragment
Rejected: Starting B pre-chained | it cannot prove the apply operation changed live routing
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep the v2 fixture basename-only and preserve real process restart plus observer assertions
Tested: go test ./cmd/lattice-agent -run '^TestLinechainE2E(Apply|Restart|Active)Helper$' -count=1
Not-tested: Full real-binary lane pending integration with worker-2 final v2 Manager head
A leased linechain task must be resolved by the linechain journal manager before generic interrupted-result synthesis; generic and NetGuard entries retain legacy recovery behavior.\n\nConstraint: E3 execution outcome is owned by the linechain transaction journal\nRejected: synthesize unknown result in generic outbox path | can conflict with terminal journal\nConfidence: high\nScope-risk: moderate\nTested: go test ./internal/taskoutbox\nNot-tested: full cross-check matrix and real E2E
Decode only the basename-bound v2 wire contract, derive all host paths from the configured runtime layout, and keep ordinary sidecar rewrites outside the fragment predecessor CAS. Normalize absent artifact digests consistently so create recovery can restore the exact old pair.

Constraint: E3 v2 wire documents cannot carry host paths or previous sidecar digests
Constraint: Existing v1 test fixtures are obsolete and migrate with the strict decoder
Rejected: Preserve v1 wire compatibility | the approved E3 contract requires strict v2-only decode
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Never reintroduce server-controlled artifact paths or sidecar predecessor CAS into v2
Tested: go test ./internal/linechain ./internal/taskoutbox ./cmd/lattice-agent -count=1
Not-tested: Real sing-box lane until accepted E2E increment is integrated
Exercise the exact VLESS+REALITY transport shape, make the crash boundary signal-driven, and model ordinary sidecar resync outside the E3 transaction so removal proves it tolerates unrelated metadata drift.

Constraint: E3 v2 artifact authority binds only the fragment predecessor; ordinary metadata writers may change sidecar bytes independently
Rejected: Manager-driven metadata resync | it hides the required independent-writer drift case
Rejected: Polling published files as the crash trigger | it races a fast restart and can miss the intended journal phase
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep the crash marker inside the restart helper so pair_published is deterministic
Tested: tagged helper compilation and go vet for cmd/lattice-agent
Not-tested: Unsimulated real-binary lane pending worker-2 Manager contract repairs
Canonicalize trusted runtime roots, reject trailing and legacy wire data, merge server-owned sidecar keys over ordinary metadata, and require exact task/lease/protocol agreement between bounded outbox and linechain snapshots before recovery can mutate either domain.

Constraint: Completed E3 outbox entries may outlive cleaned linechain journals; leased E3 entries may not lack them
Constraint: Ordinary sidecar top-level fields are outside E3 ownership and must survive create, replace, and remove
Rejected: Byte-level sidecar predecessor CAS | independent metadata writers make it stale by design
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Run cross-contract validation before either generic outbox or linechain recovery
Tested: go test ./internal/linechain ./internal/taskoutbox ./cmd/lattice-agent -count=1
Not-tested: Full/race/real-binary gates follow this commit
The managed linechain transaction now binds server-approved task scripts to
canonical local artifacts, validates complete journal authority across the
outbox handoff, preserves semantic sidecar ownership, and proves runtime state
before recording terminal results. Trusted task execution no longer exposes
linechain paths to ordinary or NetGuard tasks, while nofollow bounded reads and
fault-matrix coverage close recovery and rollback ambiguity.

Constraint: E3 recovery must suppress observable networking until exact outbox, journal, artifact, and runtime authority agree
Constraint: Existing ordinary sidecar fields remain locally owned and survive managed overlay updates
Rejected: Trust task and lease identifiers alone | they do not bind the approved script or desired artifacts
Rejected: Include command output in durable failures | checker output may contain credentials
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Do not bypass RunLinechain protocol selection or weaken full-journal authority capture before recovery
Tested: go test ./... -count=1; go vet ./...; go test -race -cover ./... -count=1; exact unsimulated sing-box 1.13.18 E2E; release workflow and install integrity scripts; version and compatibility output; secret scan
Not-tested: GitHub CI on this unpushed commit
The cmd package test binary now enters the same rlimit child shim as the
production agent before testing starts. This prevents a trusted E3 shell test
from recursively running the entire test suite when Runner re-execs
/proc/self/exe on Linux.

Constraint: Linux task execution re-execs the current binary before invoking the interpreter
Rejected: Increase the helper timeout | it would mask recursive test execution and keep CI flaky
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep cmd TestMain aligned with production taskexec.MaybeRunChildShim behavior
Tested: helper race test x10; go test -race -cover ./...; go test ./...; go vet ./...; exact sing-box 1.13.18 E2E with zero residual helpers; installer and release checks
Not-tested: GitHub CI for this new commit
@lr00rl

lr00rl commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by draft PR #15, which preserves the exact final source tree while reconstructing the downstream history so all Lore trailers parse natively. Replacement exact head 7a67ff9 passed CI run 31716214109. PR #14 is closed without deleting its branch.

@lr00rl lr00rl closed this Aug 13, 2026
@lr00rl lr00rl reopened this Aug 13, 2026
@lr00rl

lr00rl commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #15, which preserves the accepted final E3 node-agent history at exact head 145f3e6 and has successful current-head CI. No merge, tag, release, signing, deployment, or fleet mutation was performed.

@lr00rl lr00rl closed this Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant