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_SONAMEmatching the reviewed declaration basename. - Record ordered unique
DT_NEEDED, exactDT_FLAGS, exactDT_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_DYNobjects 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_NEEDEDnames; - at most 1 MiB dynamic string table and 256 bytes per name;
- one
DT_SONAMEexactly matching the reviewed declaration basename; - one
DT_FLAGSand oneDT_FLAGS_1value, 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
SONAMEand dependency facts. - Negative: verification replays ADR-0073 and decodes every dynamic table, so work remains proportional to the complete retained object set.
- Risk: matching
SONAMEis 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
- The accepted four-object ADR-0073 vector reconstructs four exact matching
SONAMErecords and three total ordered transitive edges. - The loader records zero dependencies;
libgcc_s.so.1recordslibc.so.6thenld-linux-x86-64.so.2;libc.so.6records the loader. - Tag, offset, size, mapping, duplicate, missing, embedded-policy, record, aggregate, and locally resealed mutations fail closed.
- 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.
- Focused, workspace, strict lint, governance, refinement, format, shell, link, parity, and static-authority gates pass before acceptance.
Related decisions
- 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.