Skip to content

feat(shipping): #549 S2-2 N03 sealed-memfd load with offline admission and a compiled N03 pin (Phase 2, P-N03 rev 3d) - #584

Merged
raylei50653 merged 24 commits into
mainfrom
feat/549-s2-2-n03-sealed-load
Oct 12, 2026
Merged

raylei50653 merged 24 commits into
mainfrom
feat/549-s2-2-n03-sealed-load

Conversation

@raylei50653

@raylei50653 raylei50653 commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • [Architecture umbrella] Separate model artifacts from Saccade runtime and release lifecycle #549 S2-2 N03 Phase 2: the N03 operator library is loaded from a sealed memfd. The source is opened once with openat2, hashed, copied into a labeled memfd, sealed and read back, then dlopened from /proc/self/fd/<M> with a maps post-check, a process registry and poisoning. D-N03-5 is strict in both modes, and the loader auditor identifies each object by its kernel mapping (P-N03 rev 3c, reviewed in Draft docs(shipping): #549 S2-2 N03 TOCTOU threat model and P-N03 proposal (Phase 1) #583).
  • P-N03 rev 3d (owner-accepted 2026-10-12, O-N03-1..3): the runtime no longer parses ELF. It accepts only the compiled singleton N03 pin (shipping/n03_operator_pin.json, aa84cccd…, 3,517,632 bytes) and refuses any other operator before the registry, the open or dlopen, even one whose attestation is self-consistent. The $ORIGIN/layout rule moved unchanged to the CPU-only saccade_n03_admission tool, which no runtime target links. Configure, install and the package checker compare digests against the pin. Design: docs/reference/model_bundle_s2_2_n03_offline_proposal_549.md.
  • The runtime-identity coordinate is republished (implementation axis only, 303 → 312 files, fresh probe). The evidence is bound in docs/reference/model_bundle_s2_2_n03_phase2_549.md (+ .evidence.json).

Status (unchanged by this PR until merge and main CI): N03 MB-41 OPEN, S2-2 PARTIAL, aggregate hashed_before_load, V5 UNRESOLVED, installed package UNVERIFIED, distribution.status=local-only, owner_confirmation=null. Merge is not authorized.

Decision-layer change?

  • Touched headline presets (mamba_whole_graph*.yaml)
  • Touched schema / argparse defaults (scripts/eval/config/**)
  • Touched pipeline inject (pipeline.py set_* / private det-set)
  • Touched tracker cost / auction / relink / occ kernel
  • Touched decision docs under docs/research/tracker-decision/

Behavior change?

  • Yes. Shipping runtime behavior changes:

    • the operator load path is now a sealed memfd;
    • a plan naming any operator other than the pinned N03 is refused at Gate B (catchable DetectorLoadError, exit 2, Gate A identity kept);
    • legacy mode now also requires the pin's size;
    • lineage-only plans (cfea78…) are refused; that build no longer exists.

    Ladder on the frozen source 8b020ec0: the seven-sequence GPU replay (shipping trace/no-trace, measurement double-buffer and serial, 5,316 frames each), 7 negative controls, manifest mode, the launcher run and the failure states. Raw sections are EXACT, and MOT/trace hashes equal the feat(shipping): verify S2-2 model buffers and compatibility gates #582 replay. Formal verdicts are UNRESOLVED on this host (V5: driver 617.42 ≠ 616.92). Section equality is a local diagnostic, not qualified parity.

Adding, tightening, or retiring a check? (hook / pre_push.sh / pytest / CI)

Drift prevented:        a runtime that loads an operator other than the admitted N03, a pin that drifts from the tracked attestation, an install that pairs a binary with another operator, a shipping entrypoint built with a fixture pin
Blocked entry points:   CMake configure (n03_pin.cmake) and install (install_model_root.cmake); saccade_track POST_BUILD (check_n03_pin.cmake); ctest (operator_loader, n03_pin, n03_admission, attested-operator admission); pytest default collection (test_shipping_n03_operator_pin.py, the G2 and headline install fixtures, CLI and auditor suites)
Disposition:            fail-closed. A refusal here means another operator would ship or load.
When it fails:          update the pin only through a new admission record, review and rebuild (proposal §2); otherwise fix the build or install inputs. Only the source owner may change the pin.

Test plan

Final chain on the frozen source 8b020ec0 (results/549_s2_2_n03_phase2/verify_chain_r3d.sh, local-only):

  • CPU loader test (the runtime's loader object, fixture pin): 47 checks, 30/30 repeated runs. Mutation: with the SHA pin check disabled, 3 controls fail.
  • Admission test: 32 checks ($ORIGIN/${ORIGIN} under all five tags, layout controls, pin pairing). Production pin test: 2/2. Admission record for the real N03: admitted.
  • Both saccade_track builds carry the pin digest and no admission strings.
  • GPU native ctest: 8/8.
  • CLI suites: 617 passed, 0 skipped (GPU lease). Includes the 37-case real-loader auditor suite.
  • Seven-sequence GPU replay gpu_8b020ec0240d: memfd SHA-256 equals the pin, seals 0x2f, no operator path mapped. Reports show loaded_buffer ×3 with aggregate hashed_before_load.
  • Coordinate: fresh --run-probe capture, append-only archive, attested check passes, independent review found nothing.
  • scripts/pre_push.sh: all checks passed (pytest 5730 passed).
  • Read-only Codex reviews:
    • rev 3c rounds 1–4;
    • rev 3d round 1: 1 major, 4 minor; round 2: 3 minor. All resolved except one minor that was accepted with a recorded reason: the end-to-end caller controls for manifest_head and configure (see the doc's review table).
  • PR review at e4e93bde: P2 (package checker took an unreadable pin for an older commit) is fixed in c73ff7c1. The pin is now required once the commit's shipping CMake compiles it, with end-to-end manifest_head controls; a mutation restoring the fail-open read fails them. The change is outside the coordinate's axes, and pre-push passes. P3 (the install copies the attestation from its source after validating a snapshot) predates this PR and is tracked separately.
  • main CI on the current head.

Known residuals:

  • The package checkers' sources/strace readers do not understand the memfd load.
  • The pinned shipping entrypoint (fbab8b79…) predates the pin. An installed package enforces the pin only after a package re-pin, which this PR does not authorize.
  • The ADR 025 consumer docs still say CMake ≥ 3.18, while the shipping CMake now declares 3.19. The root build already used string(JSON) at configure.

🤖 Generated with Claude Code

raylei50653 and others added 24 commits October 11, 2026 21:12
Implements P-N03 rev 3c with D-N03-5: the shipping entrypoints name the
runtime package's operator library from the kernel mapping of
<prefix>/libexec/<entrypoint>; the detector load refuses any other
binding, reads that file once (openat2, no symlinks), hashes it, seals
it into a labeled memfd, reads it back, dlopens /proc/self/fd/<M> and
checks the mapping is that memfd. One verified operator per process.
The loader auditor identifies every mapped object from /proc/self/maps
and refuses names that do not identify their mapping unless they are
the verified operator memfd. Per-object N03 scope is loaded_buffer;
the aggregate load_verification stays hashed_before_load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds real-loader controls for the sealed-memfd load: the production
host (operator_loader.cpp) under the production auditor, through the
launcher and by direct exec; host refusal of a foreign binding
(D-N03-5); auditor refusal of post-hash source changes and of every
descriptor, alias and provenance bypass found in P-N03. Build-tree
callers that load the operator now run from an install-shaped prefix:
the preflight positive control, the two-root legacy comparison and the
parity harness (stage_runtime_prefix); the SIGKILL control waits for
the operator memfd mapping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records the sealed-memfd operator load (P-N03 rev 3c, D-N03-5 strict in
both modes), its auditor rule (§17.9), the local CPU/GPU/real-loader
evidence and the residuals: package checkers that still read the
operator by path, availability, compatibility restrictions and the
pending coordinate republication. N03 MB-41 stays OPEN until merge and
main CI; the aggregate byte scope stays hashed_before_load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 1 (Phase 2): the operator's DT_RPATH/DT_RUNPATH must not
use $ORIGIN (a memfd load would expand it under /proc/self/fd), so the
verified ELF is checked before sealing; a non-ELF object is refused
there too. After a successful dlopen the mapping is checked once; any
failure poisons the process registry and keeps the memfd open so its
number is never reused, and the registry is published only after
validation. A request prepared while an identical one loaded reuses it.
Tests: descriptor reuse through load() (fails with the post-check
removed), F_SEAL_FUTURE_WRITE-only seals, $ORIGIN fixtures, and the
production auditor's maps parser at its buffer boundaries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only implementation changes, from bc378f688f9cd71b7a72b567f0718ac3d87d153fd8b246a15e6c17c927e24e5c
to 02f4dd970c4e6bfd9a2b8a2d80e89a9c8316e431ad0a33739241a4f26201ec95: 303 to 305 files, two
new (operator_loader.hpp/.cpp) and eight modified shipping loader/auditor/
plan/driver files, no deletions. Decision surface, identity semantics,
environment and runtime inputs are unchanged.

Fresh --run-probe capture after a complete Release build of 492bb3d;
publication_complete=true. The probe digest equals the outgoing one but
is freshly computed; equality does not establish equivalence. The
append-only archive keeps the outgoing canonical bytes (SHA-256
63f7ebebb38c758a54ef58a86d75c76b1c845758217a0d0f9d1ab0adc13d0c74);
candidate SHA-256 1ed472d1e91c35dc3993e19bd9fea3cef24c9467ede3336fe723ec059e005a5a. The attested
staleness check passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebinds the evidence to source 492bb3d: CPU loader controls (30/30
runs), the post-check mutation run, GPU native ctest, the real-loader
auditor suite, CLI suites, the seven-sequence GPU replay with direct
memfd identity (device, inode, seals, SHA-256) and the republished
coordinate; records the review round 1 dispositions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2 (Phase 2): the $ORIGIN check read PT_DYNAMIC at its file
offset while the loader reads it at its virtual address, so a split
table could hide $ORIGIN. The check now reads the table through p_vaddr
within a file-backed PT_LOAD, requires the stated file offset to agree,
a DT_NULL terminator and a string table within its segment
(overflow-safe), and refuses two PT_DYNAMIC. The per-request mapping is
copied before the registry is published. CPU controls cover the split
(accepted by the previous parser), unterminated, oversized DT_STRSZ and
duplicate cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only implementation changes, from bc378f688f9cd71b7a72b567f0718ac3d87d153fd8b246a15e6c17c927e24e5c
to 8cf46e38e1f836abc4f5db2d4613dcbfa32d263ae6d72086218329ea10f796ae: 303 to 305 files, two
new (operator_loader.hpp/.cpp) and eight modified shipping loader/auditor/
plan/driver files, no deletions. Decision surface, identity semantics,
environment and runtime inputs are unchanged.

Fresh --run-probe capture after a complete Release build of 7fb8c7a
(source of HEAD 85086d5); publication_complete=true. The probe digest
equals the outgoing one but is freshly computed; equality does not
establish equivalence. The append-only archive keeps the outgoing
canonical bytes (SHA-256 63f7ebebb38c758a54ef58a86d75c76b1c845758217a0d0f9d1ab0adc13d0c74);
candidate SHA-256 a78c7ad3547b41a62dffc4eb49470e93e9eb20240b2304c2a4f80f4931f58f05. The
attested staleness check passes. The coordinate of 91a0037 (reverted,
never pushed) described an intermediate source and is not archived.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebinds CPU, GPU native, real-loader, CLI and seven-sequence GPU replay
evidence to head 85086d5 (code 7fb8c7a), records the review round 2
dispositions, the ELF split-table attack (accepted by the previous
parser, refused now) and the final coordinate republication.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 3 (Phase 2): overlapping PT_LOAD segments let the check
translate the dynamic table through an earlier segment that the loader
maps over (MAP_FIXED), hiding $ORIGIN. The check now requires ascending,
page-disjoint loadable segments whose file offsets are congruent to
their addresses, so every mapped byte comes from one segment. $ORIGIN
is also refused in DT_NEEDED, DT_AUXILIARY and DT_FILTER, whose names
the loader expands too. Controls: a CPU overlap case (accepted by the
previous parser) and a real-loader DT_NEEDED case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only implementation changes, from bc378f688f9cd71b7a72b567f0718ac3d87d153fd8b246a15e6c17c927e24e5c
to 671bc0a935dc20ca08390967cb9ee2fcffe7a7f262729e6d562876e24156459a: 303 to 305 files, two
new (operator_loader.hpp/.cpp) and eight modified shipping loader/auditor/
plan/driver files, no deletions. Decision surface, identity semantics,
environment and runtime inputs are unchanged.

Fresh --run-probe capture after a complete Release build of 0e09a85
(source of HEAD c5510fd); publication_complete=true. The probe digest
equals the outgoing one but is freshly computed; equality does not
establish equivalence. The append-only archive keeps the outgoing
canonical bytes (SHA-256 63f7ebebb38c758a54ef58a86d75c76b1c845758217a0d0f9d1ab0adc13d0c74);
candidate SHA-256 9cceaf3b44e7650616e7427968c89ff8595fa32ad839bbaac66f4381f86d7674. The
attested staleness check passes. Earlier republications on this branch
(91a0037, 001ecec) described intermediate sources, were reverted
before any push and are not archived.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebinds the evidence to head c5510fd (code 0e09a85) and the final
coordinate (implementation 671bc0a9), records the review round 3
dispositions and the overlapping-segment attack (accepted by the
previous parser, refused now).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 4 (Phase 2): a PT_LOAD with p_memsz 0 skipped the layout
rule while the dynamic-table translation could still use it, and the
loader maps its file bytes. Every PT_LOAD is now validated, zero-size
load segments are refused, and the segment end and page rounding are
checked in separate overflow-safe steps. The DT_NEEDED control pins
--no-as-needed; the docs list the validated ELF properties and leave
other ELF checks to dlopen. Controls: zero-size overlap (accepted by
the previous parser) and a segment wrapping past 2^64.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Record the reviewed rev 3d proposal (offline ELF admission, compiled
singleton N03 pin, link-time pin object, digest-only install/package
checks) and the owner's acceptance of O-N03-1..3. Mark the Phase 2
evidence at 1d0ffa1 as historical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3 rev 3d)

The runtime no longer parses the operator ELF. VerifiedOperator::prepare
requires the plan's SHA-256 (and Gate A's size when known) to be the
compiled N03 pin before the registry, the open or dlopen, and the source
to have the pin's size and digest.

- shipping/n03_operator_pin.json is compiled at configure time into
  saccade_shipping_n03_pin; configure and install refuse a pin that is
  not the tracked attestation's op_library; saccade_track's POST_BUILD
  requires the pin digest; check_shipping_package compares the packaged
  operator with the pin.
- The loader is its own library linking the production pin; CPU tests
  link a fixture pin ahead of it, so they test the runtime's object.
- The $ORIGIN/layout rule moved unchanged to saccade_shipping_n03_admission
  and the CPU tool saccade_n03_admission (record on the pinned bytes); no
  runtime target links it. Its fixture controls moved with it, plus the
  braced form and all five expanded tags.
- Tests: unknown self-consistent operators are refused by the pin (CPU,
  real-loader auditor suite, CLI stand-in bundles); constructor sentinel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install_model_root.cmake now requires SACCADE_N03_OPERATOR_PIN and refuses
an attestation whose op_library is not the pin; the fake repository gets
a pin, and a control refuses a pin of another digest or size before any
file is copied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- install_model_root.cmake compares against the pin this build compiled
  into the runtime (SACCADE_N03_BUILT_PIN_*), refuses a source pin
  changed since the build, and requires the attestation to be that pin;
  configure and install share cmake/n03_pin.cmake.
- shipping CMake minimum 3.19 (string(JSON) at configure).
- saccade_n03_admission reads through one descriptor: fstat's size, then
  end of file; any read error refuses.
- check_shipping_package.check_n03_pin, with behavioral controls; rule
  controls for mismatched and malformed pins via cmake -P.
- The headline-publication install fixture gets a pin.
- Tighter controls: exact pin diagnostics in the auditor and CLI tests, a
  size refusal before registry reuse, a constructor sentinel case that
  attempts load() after the refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- install_model_root.cmake checks the attestation snapshot it copies
  against (n03_pin.cmake takes the JSON text, not a path), and requires
  this build's saccade_track, when the tree takes it, to carry the
  pin's digest (a reconfigure without a rebuild cannot pair the binary
  with another operator); a pinned entrypoint predates the pin and is
  not checked.
- Controls for the entrypoint binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only implementation changes, from bc378f688f9cd71b7a72b567f0718ac3d87d153fd8b246a15e6c17c927e24e5c
to 5eb66833ac4556b3cac7b49ddc59ec0597fa761369e69c8ebe9b3b547650f773: 303 to 312 files. Nine added (n03_operator_pin.json,
cmake/n03_operator_pin.cpp.in, cmake/n03_pin.cmake, cmake/check_n03_pin.cmake,
operator_loader.hpp/.cpp, n03_admission.hpp/.cpp, tools/saccade_n03_admission.cpp)
and nine modified (shipping/CMakeLists.txt, cmake/install_model_root.cmake,
detector_host.hpp/.cpp, detector_plan.hpp/.cpp, loader_audit.c,
tools/saccade_detector_probe.cpp, tools/track_driver.hpp), no deletions:
the #549 S2-2 Phase 2 sealed-memfd load and its P-N03 rev 3d revision
relative to main's canonical. Decision surface, identity semantics,
environment and runtime inputs are unchanged.

Fresh --run-probe capture after a complete Release build of 8b020ec
(build/runtime_identity); publication_complete=true. The probe behavior
digest 2dabed0b... equals the outgoing one but is freshly computed;
equality does not establish equivalence (equivalence stays unproven).
The append-only archive keeps the outgoing canonical bytes (SHA-256
63f7ebebb38c758a54ef58a86d75c76b1c845758217a0d0f9d1ab0adc13d0c74);
candidate SHA-256 2331fd8031eb626834e7a329e80f1c2fec25d7d897585af46d158cc1b84aceac.
The attested staleness check passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Final local chain on the frozen source 8b020ec: CPU loader 47 checks
30/30, admission 32 and pin 2 checks, the admission record for the
pinned N03, GPU native ctest 8/8, CLI suites 617 passed / 0 skipped, and
the seven-sequence GPU replay (raw sections EXACT, formal verdicts
UNRESOLVED under V5, memfd SHA-256 = the pin). The coordinate
republication bc4e77c passed its independent review. Rev 3c runs are
kept as historical evidence. N03 MB-41 stays OPEN, S2-2 PARTIAL,
aggregate hashed_before_load, V5 UNRESOLVED, local-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
manifest_head took any failed git show of shipping/n03_operator_pin.json
for a source commit before P-N03 rev 3d and skipped the check. The pin is
now required once the commit's shipping CMake compiles it
(read_n03_pin); an unreadable or non-JSON pin refuses. End-to-end
manifest_head controls on a fake source commit: the pinned operator
passes, an unreadable pin, another digest or a malformed pin refuse, a
commit whose build compiles no pin has none to check. Restoring the
fail-open read fails the unreadable-pin control. Outside the runtime
coordinate's axes (scripts/native/).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@raylei50653
raylei50653 marked this pull request as ready for review October 12, 2026 04:03
@raylei50653
raylei50653 merged commit 4a79e50 into main Oct 12, 2026
8 checks passed
raylei50653 added a commit that referenced this pull request Oct 12, 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