ADR-0071: Independent worker ELF dependency inventory
Status: accepted
Date: 2026-08-03
Decision owners: NAUX architecture and x86-64 artifact boundary
Context
ADR-0070 proves that the exact caller-reviewed worker bytes reach
execveat(AT_EMPTY_PATH) through an immutable descriptor. A dynamically linked
ELF can still name an interpreter and shared libraries that the kernel/loader
resolve outside that sealed artifact. Those undeclared runtime identities are
dependency debt, not sovereign closure.
Decision drivers
- Decode the exact sealed ADR-0070 bytes without
readelf, libelf, the host loader, LLVM, or the historical NAUX standalone parser. - Fail closed on unsupported ELF class, endianness, machine, file type, program-header shape, segment type, bounds, overlap, W+X load, executable stack, malformed dynamic table, search-path tag, or string ambiguity.
- Inventory every program header and dynamic entry, plus the exact
PT_INTERP,DT_NEEDED,DT_FLAGS, andDT_FLAGS_1facts. - Bind the inventory to the reviewed artifact expectation and ADR-0070 policy root, and require the sealed artifact again for independent verification.
- Keep dependency resolution/admission, execution, sandbox, standalone, timing, claim, and global encoder-policy authority closed.
Decision
Introduce a proof-only Linux x86-64 ELF64 decoder. It accepts only PIE
ET_DYN, little-endian ELF64 for EM_X86_64, exact canonical header widths,
bounded program and section-header tables, a supported segment allowlist,
non-overlapping ordered PT_LOAD spans, one non-executable stack, one
interpreter, one dynamic segment, and one RELRO segment.
The decoder maps DT_STRTAB through exactly one file-backed load segment,
parses a bounded NUL-terminated string table, rejects embedded search paths,
and records every ordered dynamic entry and every unique DT_NEEDED name.
Evidence is artifact-specific. Verification re-reads the opaque sealed
ADR-0070 artifact, recomputes the entire inventory, and compares it exactly;
an evidence seal alone cannot replace source replay.
Frozen limits
- Linux x86-64 ELF64 little-endian PIE only;
- at most 64 program headers and 4,096 section headers;
- at most 16 load segments and 4,096 dynamic entries;
- at most 64 dependencies;
- at most 1 MiB dynamic string table and 256 bytes per interpreter/dependency name;
- no W+X load, executable stack,
DT_RPATH,DT_RUNPATH, audit, filter, or auxiliary loader policy; - proof-only inventory; global target encoder policy remains
1.4.0.
Consequences
- NAUX can state exactly which external loader names the reviewed worker asks the host to resolve instead of treating “the binary hash” as dependency closure.
- A clean inventory still does not attest the interpreter or libraries found at launch time. Those objects and the loader’s resolution semantics remain future authority work.
- Static/freestanding artifacts may require a separate profile rather than weakening this deliberately dynamic-worker contract.
- Dependency inventory and structural hardening facts are not performance evidence.
Acceptance gates
- The exact reviewed ADR-0070 worker decodes and independently replays with a nonempty absolute interpreter and bounded unique dependencies.
- ELF identity, table width/count/range, segment type/flag/alignment/overlap, W+X, executable-stack, interpreter, dynamic table/string map/name, search path, and cap mutations fail closed.
- Receipt/evidence and locally resealed inventory mutations fail when replayed against the unchanged sealed artifact.
- Production imports no native emitter, historical raw/process/standalone/ measurement module, external ELF parser, or process launcher.
- Focused, workspace, strict lint, governance, refinement, format, shell, and static authority gates pass before acceptance.
Accepted evidence
The independent inventory policy is accepted under root
1a6c96c8b47a2001d9969488785c6bb2b846c378280b0498f2c16b0f14fd3bbf.
The root binds schema/policy 1.0.0, all ELF/table/string/dependency limits, and
the accepted ADR-0070 sealed-artifact policy root.
For the reviewed acceptance worker, independent replay reconstructs 12 program
headers, four ordered load segments, 29 dynamic entries, the absolute
interpreter /lib64/ld-linux-x86-64.so.2, and three ordered direct dependency
names: libgcc_s.so.1, libc.so.6, and ld-linux-x86-64.so.2. DT_FLAGS is
0x8 (BIND_NOW) and DT_FLAGS_1 is 0x08000001 (NOW | PIE). The decoder
rejects W+X loads, executable stacks, embedded loader search policy, malformed
or ambiguous strings/mappings, noncanonical DT_NULL, truncation, and locally
resealed evidence mutation.
Focused positive and structural-mutation tests pass 2/2. The workspace unit suite passes 408 tests with zero failures and six explicit release-only ignores; the native/process/standalone parity tail, strict Clippy, format, perf-gates, and independent ADR-0069/0070 boundaries pass. The artifact and inventory evidence hashes remain deployment-specific; only the policy root is global.
This accepts declaration inventory, not dependency admission or closure. The interpreter and shared objects selected by the host loader are still unresolved external identities.