ADR-0074: Sealed-object dynamic identity inventory

Status: accepted
Date: 2026-08-09
Decision owners: NAUX architecture and x86-64 transitive declaration boundary

Context

ADR-0073 retains exact reviewed interpreter and direct-dependency bytes in private immutable descriptors. Exact bytes and basic ELF identity still do not state which loader identity each object declares internally or which additional objects its dynamic table requests. Treating the direct vector as closure would therefore be an unsupported trust claim.

Decision drivers

  • Decode only bytes obtained from an independently verified opaque ADR-0073 object set; never reopen its reviewed source paths.
  • Use a decoder independent of the ADR-0071 worker inventory so one parser implementation cannot certify both boundaries.
  • Require one exact DT_SONAME matching the reviewed declaration basename.
  • Record ordered unique DT_NEEDED, exact DT_FLAGS, exact DT_FLAGS_1, dynamic-entry count, string-table extent, offsets, and object provenance.
  • Reject ambiguous virtual-to-file mappings and embedded search, audit, filter, or auxiliary loader policy.
  • Inventory declarations only. Do not resolve names or claim closure, mapping, relocation, loading, or execution.

Considered options

Use readelf, libelf, or the host loader

Rejected. This creates external semantic authority and permanent dependency debt at the exact boundary intended to measure that debt.

Reuse the ADR-0071 decoder

Rejected. Reuse reduces code but correlates parser bugs and ADR-0071 assumes a PIE worker with a nonempty direct dependency vector. Shared objects need a different contract and may validly declare zero DT_NEEDED entries.

Trust filenames as SONAME

Rejected. A reviewed locator or declaration can disagree with the identity embedded in the admitted bytes.

Independently decode every sealed object

Selected. Verification first fully replays ADR-0073, then reads each immutable descriptor internally, maps its dynamic string table through exactly one file-backed load segment, and reconstructs the complete evidence vector.

Immediately claim transitive closure

Rejected. Inventory exposes a graph but does not yet admit a reviewed closure policy, resolve names to bytes, or reproduce host-loader selection semantics.

Decision

Introduce a proof-only ordered dynamic evidence vector with exactly one record per ADR-0073 object ordinal. Each record binds the predecessor object-evidence root, exact object digest, reviewed declaration, matching DT_SONAME, ordered unique DT_NEEDED names and offsets, both dynamic flag words, dynamic table cardinality, string-table extent, and a domain-separated record hash.

The decoder independently validates ELF64 little-endian x86-64 program-header identity, bounded load layout, exactly one dynamic segment, a canonical NUL-terminated dynamic table, exactly one DT_STRTAB, DT_STRSZ, DT_SONAME, DT_FLAGS, and DT_FLAGS_1, and an unambiguous file-backed string mapping. DT_RPATH, DT_RUNPATH, audit, filter, and auxiliary tags fail closed.

Frozen limits

  • Linux x86-64 ELF64 little-endian ET_DYN objects only;
  • at most 65 ordered objects;
  • at most 64 program headers and 16 load segments per object;
  • at most 4,096 dynamic entries and 64 ordered unique DT_NEEDED names;
  • at most 1 MiB dynamic string table and 256 bytes per name;
  • one DT_SONAME exactly matching the reviewed declaration basename;
  • one DT_FLAGS and one DT_FLAGS_1 value, recorded without reinterpretation;
  • no RPATH, RUNPATH, audit, filter, or auxiliary loader policy;
  • accepted ADR-0073 policy root c9780ea71b48becf884b4bc4bc1963fa6794b4da4c83c5486bc3ae70c3737bc9;
  • proof-only inventory; encoder policy remains 1.4.0.

Consequences

  • Positive: every exact direct object now has a byte-bound internal identity and an explicit ordered transitive declaration vector.
  • Positive: duplicate reviewed appearances of the same loader independently reconstruct the same SONAME and dependency facts.
  • Negative: verification replays ADR-0073 and decodes every dynamic table, so work remains proportional to the complete retained object set.
  • Risk: matching SONAME is not proof that the host loader selects these bytes; a separately reviewed closure and resolution policy is still required.
  • Open: closure admission, symbol/version requirements, relocations, loader resolution order, mapping state, execution, and sandboxing remain outside.

Acceptance gates

  1. The accepted four-object ADR-0073 vector reconstructs four exact matching SONAME records and three total ordered transitive edges.
  2. The loader records zero dependencies; libgcc_s.so.1 records libc.so.6 then ld-linux-x86-64.so.2; libc.so.6 records the loader.
  3. Tag, offset, size, mapping, duplicate, missing, embedded-policy, record, aggregate, and locally resealed mutations fail closed.
  4. Source deletion cannot change verification, and production imports no path resolver, process launcher, host loader, external ELF parser, native emitter, historical standalone/raw decoder, or measurement authority.
  5. Focused, workspace, strict lint, governance, refinement, format, shell, link, parity, and static-authority gates pass before acceptance.
  • ADR-0071 inventories the sealed worker through an intentionally separate decoder.
  • ADR-0072 admits the worker’s reviewed declaration vector.
  • ADR-0073 supplies the opaque immutable object bytes consumed here.

Accepted evidence

The dynamic-identity inventory policy is accepted under root 7083c3d4b4d4afed21023448d8a16b066558a6e3f17969835e1a87892a3d371f. It binds schema/policy 1.0.0, the accepted ADR-0073 root, every object/header/ load/dynamic/string/name limit, exact entry widths, and nine named capabilities covering opaque sealed input, independent decoding, SONAME agreement, ordered transitive inventory, hardening flags, forbidden loader policy, and the proof-only authority ceiling.

The accepted four-record vector reconstructs, in order, SONAMEs ld-linux-x86-64.so.2, libgcc_s.so.1, libc.so.6, and ld-linux-x86-64.so.2. Both loader records have zero DT_NEEDED entries; libgcc_s.so.1 requests libc.so.6 then ld-linux-x86-64.so.2; libc.so.6 requests ld-linux-x86-64.so.2. The aggregate therefore records exactly three ordered transitive edges, without yet minting closure authority.

The focused ADR-0073/0074 carrier passes 2/2, including seven dynamic decoder mutations, 23 lower ELF mutations, source deletion, evidence/root drift, and locally resealed record failure. The complete workspace passes 408 library tests with zero failures and six explicit release-only ignores plus every enabled integration and doc test. Strict Clippy and format pass. Only the policy root is global; object and dynamic evidence roots remain deployment- specific.

Acceptance inventories byte-bound internal identities and transitive names. It does not admit graph closure, reproduce host resolution, or grant mapping, relocation, execution, sandbox, timing, performance-claim, or global encoder- policy authority.