Dev log

Building Agent Passport System, day by day.

Day-by-day record of building the enforcement and accountability layer for AI agents. Bring your own identity: did:key, did:web, SPIFFE, OAuth, native did:aps. Started February 18, 2026. 6,003 tests, nine papers, IETF draft. Open source. Full surface area: 152 MCP tools.

Day 114, later the same night: 2.6.0 goes stable.

The 2.6 line is on npm latest as of tonight. A plain npm install agent-passport-system now delivers what the alpha channel carried for six weeks: the Wave 2 evidence layer with its verifier-derived descriptors, trust-root policy, revocation enforcement, audience binding, selective disclosure, and the offline verifier; Context Provenance Attestation; the action_ref v1 conformance set; and the payment-rail surface through today's envelope-authenticity check. 3,791 tests, zero failures, on the exact tree that published.

The Day 105 entry said the stable cut waits on two things: a content-derived decision identifier, and batch-verify parity running green rather than environment-gated. Both closed today. decision_id is now a domain-separated content hash over the record's identity fields, recomputable offline by any verifier from disclosed fields alone, and the field taxonomy that should have existed from the start now does: identity fields are path-independent, ordering fields state their scope, event-instance fields are disclosed and MAC-bound. With that in place the parity claim stopped being a test that could fail and became a property that cannot: a single check and a batched check return byte-identical decision records under a pinned clock, exercised in the production binding before the merge, not in a lab wiring that production never ships. The number that is published is still the one that can be defended. What changed tonight is how much it defends.

Day 114: An outside contributor ships the check the last review asked for, and the site starts carrying its own terms.

The Day 104 review of the Cycles binding check ended with a boundary stated out loud: a passing check meant bound, not authentic, and the result was asked to say so plainly. Today the contributor behind that rail shipped the next step (#45): the supplied evidence envelope's own Ed25519 signature is now verified against the key the envelope names, under the spec's signature derivation, which is a different canonicalization from the content hash, the signature field emptied and the identifier left populated. Failure is a distinct fail-closed reason, and a passing verify now reports which guarantee actually held: signature over the named key, or that plus a pinned receipt issuer. The part worth pointing at is a test the contributor wrote that asserts a fully self-consistent forgery, re-signed by an attacker's own key, still passes with only the weaker tag. The gap that remains, resolving whether the named key is the legitimate signer, is tracked on the Cycles side rather than papered over here. The branch predated a clock fix on main, so the suite was run on the merge result, which is the thing that ships: 3,791 tests, zero failures.

On A2A, the cache question from the signing thread resolved the right way. The original three-value key-source enum treated cache as a place, which is a laundering hole: a hot resolver fetch becomes "cache" one call later and the provenance of the original resolution is gone. The fix that held is defining cache as a derivation, previously verified through an allowlisted path and pinned, with the population event, source and timestamp, auditable from the signed evidence rather than from the verifier's word. The CTEF author folded that definition into the CTEF key_source and the v0.4 transactional receipt. The enum stays at three values; the trust posture becomes reconstructable offline.

And the protocol's own site now carries the thing it asks publishers to carry. agent-passport.org serves a signed governance declaration at /.well-known/aps.txt: a fresh Ed25519 domain keypair, the same machine-readable terms as the original declaration on the prior domain, verifiable offline from the live bytes. The honest part of this entry is why it happened today. The site migration had left a placeholder at that path, and a comment elsewhere cited the file as live before anyone re-checked it. A protocol built on the claim that statements should be checkable does not get to leave its own cited statement unverifiable, so the fix was not an explanation. It was making the cited claim true.

Day 113: The correlation key gets one exact form, and evidence starts carrying where it came from.

The cross-ecosystem correlation key from Day 106 is now a frozen specification with conformance vectors (spec page). Four fields in the preimage, and the field that caused every interoperability argument, the timestamp, is pinned to one exact representation: RFC 3339 UTC with exactly three fractional digits, uppercase T and Z, zero-padded, one valid byte sequence per instant, hashed as opaque bytes and never normalized. The vectors enforce that choice from both directions: the negative vectors reject every non-canonical timestamp form, and two of the accept vectors byte-match hashes published by independent ecosystem implementations, so a pass demonstrates cross-implementation agreement rather than self-consistency. There are two verifiers because the failure modes differ: a stdlib-only Python verifier that vendors its own minimal RFC 8785 serializer and recomputes every hash with no dependency on this project, and a Node verifier that imports the shipping helper, so the vectors stay pinned to the code that actually runs rather than a reimplementation of it. The non-goals are part of the spec: a pass proves byte-exact agreement on the derivation, and nothing about whether an action was authorized, occurred, or honored its scope. Those claims belong to the records the key correlates.

The evidence layer learned two things in the same cut. Verification-source provenance now travels inside signed evidence: when a verification result is carried as evidence, the record of how the key behind it was resolved rides in the signed envelope, not in the verifier's logs, because a claim a verifier makes about itself is exactly the kind of claim this layer exists to replace. And the producer-attestation commitment kind landed with a slot for a Context Provenance Attestation, connecting Day 107's context custody to the evidence chain: an action receipt can now carry a committed reference to the declared basis the action was built on, with the same stated boundary as before. Custody of the declared basis, not truth of it.

Day 107: Custody for the context an action was built on, and the protocol in a third language.

Context Provenance Attestation v0.1 is exported from the public API as of [email protected]. A CPA is a signed, partitioned Merkle commitment to a declared context basis. The producer groups the context an action was built on into eight frozen structural-origin channels (system-config, developer, user-socket, retrieval-store, tool-result, external, memory, quarantine), builds one Merkle partition per channel, commits the partition roots into a single top root, and signs the result. The channel is a partitioning key, not a trust label. There is no field where a producer writes "trusted" or "clean" as a claim about origin, and the channel is part of the leaf preimage, so relabeling it breaks the root. The hashing is domain-separated with distinct tags for leaf, node, and signing, and odd nodes are promoted without duplication per RFC 6962, which closes CVE-2012-2459. Two disclosure modes: full-set, where every leaf is revealed and completeness is proven against the signed tree, and inclusion, where counts plus a sampled subset come with inclusion proofs and completeness is not proven by design. The CPA and the action receipt each carry the other's reference, so a stale CPA and a stale receipt cannot be silently re-paired. The verifier is offline and fail-closed, with structured reason codes instead of one opaque failure.

The boundary is the whole point, so it is stated rather than implied. A CPA proves custody of the declared basis as of the producer-stated time, tamper-evidence over the committed structure, and replay resistance. It does not prove the context was true or clean, and it does not prove faithful capture: that the declared basis equals what the model actually conditioned on. A well-formed CPA over basis A, when the model conditioned on B, still verifies. It is a flight recorder, not a seatbelt. Faithful capture needs an independent capture boundary, write-time receipts or a TEE or runtime instrumentation, and that is the one open vector, named and deferred rather than left implicit. One line of the custody claim got tightened during the work, from an absolute timestamp to producer-stated time, because the producer is the only party that can attest when it assembled the basis.

The same cut put the protocol into a third language. agent-passport-go reached v0.2.0-alpha.1, fifteen packages across the surface. The verify path compiles no key code at all, which is the right shape for an infrastructure sink or proxy that checks receipts without ever holding a private key, and v0.2 added the issuing side: passport, delegation with narrowing and signed revocation, completion receipts, attribution Merkle with beneficiary tracing, the values floor, coordination, commerce, and in-toto decision receipts. CPA shipped with a cross-language Go parity fixture, so the two implementations can be checked against the same bytes. On parity I want to be exact, because exactness is the point of the project: the Go primitives are pinned against shared canonical-JSON and CPA fixtures, which is a real guarantee and a smaller one than the continuous cross-build parity the TypeScript and Python implementations hold across twenty-seven scenarios. Pinned-to-fixtures is the claim, so pinned-to-fixtures is what I am saying.

Day 106: A correlation key is only worth defining once, by no one in particular.

The join key that lets two parties prove their receipts refer to the same action showed up in two more independent implementations this week. It is a small thing by construction: the SHA-256 of the JCS canonical form (RFC 8785) of the intent tuple, recomputable from the disclosed fields alone. raucle-detect published canonicalization test vectors and added one in the four-field shape; recomputing it under the APS canonicalizer reproduces the published digest byte for byte. argentum-core carries its own implementation of the same derivation. Three implementations, one digest, none of them the source of the others.

That last point is what the W3C auditability thread (#34) settled this week, and it meant withdrawing something said earlier. If a correlation key's whole value is that any verifier can recompute it without trusting whoever issued the record, then it cannot be defined by reference into any single implementation draft, including this project's. The derivation belongs in the community-group text itself, with the implementations listed underneath as references rather than as the definition. The earlier position that named the APS draft section as the source was the dependency the layer exists to remove, so it came out. The honest open item is that the field set is the specification: JCS canonicalizes JSON structure, not value semantics, so the tuple has to pin types and precision, a timestamp as an epoch integer and the same instant as an ISO-8601 string produce different digests. Requested scope was removed from the key as well, because scope narrows during a decision, and binding the key to the requested scope would force disclosing the original broad request to verify a narrowed receipt, which defeats the selective disclosure the layer is there to protect. Those two went back to the thread as the review items.

Day 105: The evidence layer ships to the alpha tag, and a descriptor that refuses to score itself.

The second wave of v2 modules went to the prerelease tag on npm, twelve of them covering evidence description, trust-root policy, a remote signer, revocation enforcement, audience binding, human oversight, hash-and-pointer selective disclosure, a scope-dimension registry, and an offline verifier with a conformance runner. It published to alpha (2.6.0-alpha.9), not to latest, which still points at 2.2.0. That is deliberate: the stable cut waits on finalizing a content-derived decision identifier and on the native batch-verify parity running green in a capable build rather than being gated by the environment. The number that is published is the one that can be defended.

The load-bearing design choice is in the evidence descriptor. It is derived by the verifier, not asserted by the issuer. It records mechanical facts, who signed, whether that signer is independent of the key graph that vouches for it, and a four-valued corroboration status, and it does not emit a scalar assurance score. A test feeds it a spurious assurance field and asserts the field never reaches the output. The reasoning is the same one that runs through the rest of the protocol: a number a subject can set about itself is a number it can inflate, so the descriptor reports only what an independent verifier can derive, and leaves the claim of trustworthiness to the party doing the checking.

Day 104: Three records an auditor can walk, and a binding check that says what it does not prove.

The W3C accountability-gap thread (#34) converged on a shape, so the subsection proposal went in. It builds an accountability record from three parts along the life of an action. A commitment says who was authorized to do what, and under which delegation chain. A decision says what authority and context were evaluated at the moment of action. A receipt says what actually happened, signed after the fact. Each part references the next by content hash, and none embeds the other, so every part stays independently verifiable and an auditor can ask for only the layer it needs. The serialization rides on plain W3C verifiable credentials linked by digest, with no new envelope format to adopt. The reasoning the agent applied binds inside the decision record. Whether the underlying claim was true stays in a separate belief layer, referenced and not inlined, because these records carry admissibility evidence and do not assert truth.

A second thread (A2A #1463) asked a neighboring question, in-task authorization through an OID4VP extension. OID4VP proves who an agent is and that it presented a qualifying credential. It does not prove that the action the agent then took sat inside a sound authorization policy. In APS that second question is the scoped delegation chain, where each hop can only narrow authority, so the envelope a verifier checks against is the intersection along the chain, and a gateway evaluates the concrete call against it before execution. The credential and the delegation envelope reference each other by content hash. OID4VP answers who, and qualified. Scoped delegation and enforcement answer whether the action was allowed. The contributor confirmed the same attenuation model from their side.

On the Cycles payment rail, a contributed verification check (#42) earned a careful review. It binds an APS receipt to the specific Cycles evidence envelope the receipt points at, by recomputing the envelope's content hash and confirming it matches both the envelope's own id and the hash the signed receipt committed to. That recompute reproduces the Cycles server's identifier byte for byte, so the binding is real and tampering with the envelope is caught. The honest part is the boundary. The check proves the receipt is bound to this envelope, and that the envelope is internally consistent. It does not verify the envelope's own signature against a Cycles signing key, which is a separate step now tracked for the next cut as issue #43. A passing check means bound. It does not yet mean authentic, and the review asked the result to say so plainly rather than let a reader infer more.

Day 103: The site says where the protocol plugs in, and the Cycles receipt carries the authority it saw.

The landing page got the section it was missing. "Where it plugs in" sits between the open-source case and the integration grid, and the grid lists the real connections without inflating them. Payment rails carry spend authority tied to a signed delegation, with a receipt per charge, through bindings for x402, Google's AP2, ACP, and Stripe issuing. Agent protocols carry identity and scoped authority with the call, exposed as MCP tools and carried across A2A by an adapter. On the standards side, gitagent-protocol merged the optional cryptographic identity layer for agent manifests. Frameworks get the passport riding into the runtime through LangChain, CrewAI, and AutoGen adapters. The line under the grid is the part that matters. Some of these are bindings the project ships, others are contributions merged upstream, and none of it is a claim that these ecosystems depend on APS. Connective tissue, stated as connective tissue. The same pass moved the remaining pages onto the dark theme with one canonical nav and footer.

On the Cycles payment rail, the permit receipt picked up a field it needed. authority_state_at_admission records whether the delegation was revoked or expiring at the moment the reservation was admitted, signed into the receipt body rather than left to a separate lookup. A verifier reading the receipt offline now sees the authority state the gateway saw when it said yes, without trusting a second system to still agree later. The contributor driving the Cycles adapter ran a verb-shape pass over the budget_reservation crosswalk in the same stretch, confirming the reserve, permit, release, and refund shapes line up with what the Cycles surface actually emits.

One older thread closed a loose end. On the AIVSS enforcement-effectiveness review (#4), the reviewer confirmed the bound-parity language reads clean. Two substrates whose security windows land inside the same bound establish that the dimension measures the methodology, not the backend, so the substrate drops out of the comparison. It is a small fix, but it was creating a reader contradiction, and the v0.2 cut needed it settled before the remaining two items land.

Day 101: Resume-time enforcement modes, and an auditability gap that walks by hash.

A thread on Microsoft's agent-governance toolkit (#1609) put a sharp edge on the LangGraph adapter. When a graph pauses at a checkpoint and resumes later, the authority that was valid at pause time can be narrowed or revoked by the time it resumes. The receipt model answers that without replaying the verifier. A resumed action keeps its actionRef but produces a different compoundDigest when the evaluation outcome changes, and the mismatch lands on the signed PolicyReceipt. A contributor moved the design forward by putting delegation_chain_root in LangGraph's checkpoint metadata rather than the user's state schema, so governance fields never collide with application state. The open question was whether the adapter should hard-block on stale authority or emit an event and defer to policy. In the SDK that is neither a binary nor the adapter's call. Enforcement mode is a property of the principle being evaluated: inline blocks, audit records, warn warns, ordered by escalation. The verdict that matters at resume is the third one. narrow clamps a resumed action to whatever authority is still valid instead of killing the graph, which is usually what a production run actually wants.

The W3C AI Agent Protocol community group opened an auditability-gap RFC (#34) around three questions an auditor has to answer about any agent action: who authorized it, what was decided, and what was committed. APS answers all three with content-addressed artifacts that reference each other by hash instead of by embedding. The intent carries a content hash of its unsigned form. A compound digest binds the hash of the intent and the hash of the policy receipt. A receipt can name the previous receipt by hash. An auditor walks authority to decision to commitment to receipt by checking hashes and canonical forms, with no requirement that every participant run the same software. The direction that came out of the thread for the white-paper subsection is vocabulary-first: state the gap in protocol-neutral terms, then place APS and the other framework underneath as implementation bindings, so the section is usable by anyone who agrees on the gap without adopting a stack to read it.

Two A2A threads carried the same posture on the identity side. #1850 added an Identity Trust Framework roadmap that keeps the native draft-pidlisnyi-aps-01 §4.1 action_ref as the primitive APS receipts sign, while emitting or carrying the shared external action-ref-v1 correlation key, so a receipt keeps its own preimage and still lines up on the cross-system key. #1628 consolidated the trust.signals[] extension into one signal-type specification rather than a scatter of per-vendor fields, so a downstream verifier has a single shape to route on. The pattern across all four threads is the one the project keeps returning to. The framing changes, the primitive does not, and the work is showing that the primitive already answers the question.

The work that made substrate cycle 2 possible.

The three signal_type modules that shipped in alpha.6 tonight stand on a stack of work by people across the ecosystem who pushed for the primitives, contested the framing, and shipped the implementations that make composition meaningful. This post records who built what, and which artifact each substrate module reaches into.

The memory_provenance envelope traces directly to the OWASP ASI06 (memory and context poisoning) entry in OWASP Top 10 for Large Language Model Applications. The MINJA and MemoryGraft research lines named the threat model the envelope addresses. VeloGerber drove the AIVSS review pass at OWASP AIVSS #31 and #33 which led to the boundary distinction between detection and provenance recording that the SDK side enforces.

The cross_issuer_attestation envelope is downstream of the four-layer composition codification that Libria shipped across A2A #1575, #1786, and #1829 in May, and of the @envoys/sdk RFC 9421 composition fixture that jschoemaker published with byte-identity test vectors. The federation primitive in the SDK exists because the ecosystem already produced working cross-implementation signatures to reference. kenneives contributed the CTEF cross-link discipline that makes attestation provenance traceable across documents. MoltyCel (CryptoKRI GmbH) has run the broadest external participation across 10 ecosystem threads, with feedback present on the shape of every composition primitive the SDK now ships.

The behavioral_drift_window envelope is the window-snapshot complement to the streaming drift family that has been in production since SDK v1.41.0, co-developed with nanookclaw at the PDR project per PDR v2.19 §6.6. The window-size analysis in that section is the empirical work that grounds both families. The vocabulary entries that name the canonical primitives (divergence_signal, baseline_revision, observation_window, trust_velocity, decision_lineage) carry issuers_in_production: ["PDR", "APS"] for the same reason: two production implementers, two independent code paths, one canonical shape.

Beyond the three modules, the cycle-2 ship depended on contributions that landed earlier in May. giskard09 shipped argentum RFC 001 Active and ran the budget_reservation Promotion-path verification work that anchored the canonical-promotion pattern. amavashev shipped the Cycles payment-rails adapter v0.1 (PR #39, +19 tests) that landed on origin/main during cycle 1 today, with 1,330 lines across 8 files. desiorac remains the substrate's unpaid QA, with 251 substantive comments in 3 threads. agent-morrow at AWS and imran-siddique at Microsoft are present on the W3C and A2A surfaces where the protocol's interop posture gets contested.

The full contribution map covers 172 participants across 98 GitHub threads with 2,695 comments tracked. The public view of the ecosystem, as a directory of who is building what and how the projects compose, lives at the Agent Ecosystem Directory (CC-BY-4.0 data, MIT code). The contribution map's full per-person and per-topic data stays internal because the ranking methodology depends on keyword heuristics that misclassify individual contributions in ways the directory's sortable filters do not. The directory is the public artifact. The contribution map is the working tool.

If you contributed to one of the substrate primitives and your name is not in this post, the omission is mine and the fix is one PR or issue away. The discipline is to credit the work that made the artifact possible, not to claim the artifact alone. Substrate cycle 3 will build on this one. The pattern holds.

Day 99: Substrate cycle 2 ships. Three new attestation envelope signal types in alpha.6.

The matrix v2 scoping post on A2A #1734 named five candidate gap classes for a cross-implementation trust-evidence-format substrate window. Two were already built in cycle 1 earlier today. Three more shipped tonight in agent-passport-system 2.6.0-alpha.6: memory_provenance, cross_issuer_attestation, and behavioral_drift_window. Each is a new signal_type with a signed envelope, validation rules, and a test file. Each holds tight v0.1 scope and pushes computation, policy, and verification of constituent integrity to downstream consumers.

memory_provenance is OWASP ASI06 substrate. The envelope traces a memory entry back to a trusted source under a declared reduction_map_ref, so a verifier can recover what was originally stored, what transformation was applied, and which agent committed it. The SDK validates Ed25519 signature and shape. Verification of the original source content against source_ref and validation of the reduction map against a registered transformation stay with the consumer. 20 tests, six structured failure reasons covering shape, ID format, signature, source-fields, timestamp, and source-hash format.

cross_issuer_attestation is the federation primitive for the envelope family. A composer agent signs a bundle of references to N constituent envelopes, where each constituent can be from a different issuer and a different signal_type. The composer signature attaches the composer to that specific bundle. Tampering with any constituent reference invalidates the composer signature. Downstream verifies each constituent independently. 10 tests, with explicit duplicate-hash and reorder coverage so the federation primitive cannot be silently rewritten in flight. Cross-protocol composition between APS and AIIF is a separate artifact, deferred to a position paper.

behavioral_drift_window is the longitudinal extension of single-event attestations like cognitive_attestation and governance_attestation. The envelope bundles N constituent attestation hashes across a declared time window with caller-supplied metric placeholders: decision_count, class_distribution, optional confidence_mean and confidence_stddev, optional baseline_ref paired with divergence_score. The SDK validates internal shape consistency only. It does not compute drift. Drift analytics stay in @aeoess/gateway per the public-private boundary that landed in April. The envelope is for audit, handoff, and point-in-time evidence, complementary to the existing streaming family (divergence_signal, baseline_revision, observation_window, trust_velocity, decision_lineage, the recentObservations ring buffer on ScopedReputation, and the DecisionLineageReceipt) in production since SDK v1.41.0 and co-issued with the PDR project per Nanook PDR v2.19 §6.6. 26 tests, twelve documented edge cases.

Test count moved from 3,008 to 3,064, across five new module directories under src/v2/. Each signal type shipped with its envelope schema, validation rules, and a dedicated test file, and the release went out to the SDK, MCP, and Python packages in one pass.

The discipline pattern that matters here is the public-private boundary holding under load. Building three modules in parallel, the one with the most product-intelligence-shaped affordances was behavioral_drift_window, and it had every opportunity to drift across the boundary. The boundary held: each metric field is a placeholder, the SDK validates internal consistency only, recomputation is named as a downstream responsibility in the verify.ts comments. The protocol stays a public primitive, the analytics stay private. The substrate window can deliver the receipt shape without the SDK quietly turning into the product.

Day 96: SpendGuard ships as third implementer of budget_reservation.

The crosswalk/budget_reservation.yaml file at agent-governance-vocabulary has carried a documented promotion path since it landed at crosswalk_type: domain_incubation on May 12. Three thresholds for moving to canonical: a third production implementer, plus a second implementer for each proposed verb. Tonight all three conditions met. SpendGuard SDK 0.4.0 hit PyPI at 19:04 UTC with a release_reservation() method matching the proposed verb names. The release brings release and refund to two implementers each, joining Cycles and goodmeta respectively. Three production implementers now anchor the file: Cycles, goodmeta, SpendGuard.

SpendGuard's maintainer opened PR #99 a few hours after the SDK release. The PR brings the reservation lifecycle state machine from SpendGuard's Agent Spend Protocol Draft-01 §3.3 upstream into the canonical crosswalk file, before promotion freezes the shape. Six states, a (reservation_id, idempotency_key) dedup contract with three required outcome branches, and a TTL grace window matching the Draft-01 "SHOULD be ≤ 5 minutes" phrasing. Status sits at proposed pending Cycles and goodmeta confirmation on the matching tracking issue. The review asked for one structural change before merge: explicit normative-force framing on the lifecycle block so the boundary between descriptive and binding is unambiguous for downstream readers.

Three implementers landing the same canonical verb set in two weeks is what the domain_incubation staging class was designed to surface. Promotion to canonical waits on each proposed verb gaining a second implementer with semantics confirmed in production, not on a contributor's claim. The file's promotion-path comment is the gate, and the gate held. The discipline that matters here is structural: the registry names the canonical verb set, contributors are credited as named implementers inside the file, no single implementer's deployment owns the category. The pattern works the same way for transport-survival fixtures, cross-implementation receipt validation, and every other normative surface the project maintains.

Day 94: HKDF receipt-stream-key derivation replaces the placeholder.

The Rust verifier's receipt-stream MAC path had carried mac_key = [0u8; 32] as a placeholder since Stream A landed. A literal zero-byte MAC key fails the per-stream uniqueness property in the most direct way possible: every stream has the same key, so cross-stream replay is trivial and recovery cannot distinguish streams. Phase 2 item 1 closes that gap. Branch phase2/hkdf-receipt-stream-key, commits 3aa9544 and dbee6b3. HKDF-SHA-256 with explicit TLV-framed info parameter replaces the placeholder at both derivation sites (compiled.rs::from_passport and lib.rs::load_passport_with_recovery).

The design discipline is in the info parameter. RFC 5869 makes that field the binding between context and derived key, so the bytes that go in are load-bearing. The set: aps:v1:receipt-stream-mac as the domain-separation tag, followed by length-prefixed delegation_chain_hash, stream_id as UTF-8 with explicit length prefix, and the four-byte big-endian revocation_epoch. The IKM is the passport's Ed25519 signature bytes, which means the IKM is publicly recomputable from the passport JSON. That is consistent with the spec's threat model boundary at receipt_log.rs: the MAC key's secrecy is not what provides above-Tier-1 protection, the integrity of the chain is. Domain separation is the property the HKDF provides.

Test count moved from 191 to 206 with the additions to tests/key_derivation.rs. RFC 5869 known-answer vectors A.1, A.2, A.3 all pass. Recovery round-trips through the new derivation in the runtime SDK tests. The Stream B (TypeScript bindings) and Stream C (benchmark harness) work resumes after this item lands. The phase-2 sequence is narrow on purpose: replace one primitive at a time, prove domain separation, do not expand into a full key-management redesign.

Day 93: Prototype 1 latency project closed across all three §13 environments.

The protocol specification names three canonical hardware environments at §13: an Apple Silicon developer reference, an AWS cloud reference, and a bare-metal Linux canonical. Numbers in public materials should cite measured results across the canonical set, not the developer-laptop figure repeated three times. Prototype 1's latency project ran the full benchmark harness across the three environments and recorded the L4 p50 (the end-to-end gateway loop covering TCP, signature verification, scope evaluation, and receipt persistence). Mac M3: 305 microseconds. AWS c7i.2xlarge: 1.07 milliseconds. Bare-metal AMD EPYC 7313P via Latitude.sh: 822 microseconds. The roughly 250-microsecond gap between AWS and bare-metal is the hypervisor-removal effect. Result JSONs reproducible from benchmarks/prototype-1/results/ at commit 6e258f4.

PRs #36 and #38 merged, closing the project. Two disciplines fell out of the work that are worth recording. First, the cpu_model pin rule for bare-metal claims: Latitude.sh ships variable EPYC SKUs under the c3-large-x86 plan, so claims that name "bare-metal EPYC 7313P" need the specific SKU pinned in the result JSON or the claim narrows to "bare-metal EPYC class." CLAIMS.md upgraded with the rule. Second, the spec-environment-names convention: every published latency number from this point on cites the §13 environment by name, not just the hardware family. "1.07ms on AWS c7i.2xlarge" rather than "1.07ms on x86-64."

The closure unblocks Stream C, the benchmark harness work that validates the latency story before Stream B builds usability on top of an unmeasured foundation. Stream A (Rust verifier) closed at 191 passing tests across 12 test suites covering wire format through crash recovery. The order matters: verify the architecture earns the latency story, then build the bindings that expose it.

Tool-registry integrity: closing the gap so the claim is true

The OASIS Coalition for Secure AI has a control proposal in review, controlToolRegistryandDiscoveryIntegrity, covering integrity of tool registries, manifests, and discovery endpoints across their lifecycle: signed manifests, publisher identity, namespace governance against typosquatting, and re-approval when tool metadata changes after vetting. The Agent Passport System already had signed registry entries and implementation-hash integrity. It did not yet have the other three pieces.

Rather than narrow what we claimed APS does, we built what was missing. Signed tool manifests with a distinct metadata hash. Publisher identity verification, resolving an external trust root or an APS-native identity, not a caller-supplied key. Namespace governance so a tool under a claimed namespace must be published by that namespace's owner. Re-approval bound to a resolved attestor identity, so a metadata change moves a manifest to pending and only a verifiable attestor can clear it, which makes the workflow cryptographically meaningful rather than only stateful. Each piece has tests, including the adversarial cases, and cross-language conformance vectors.

The work is on the cosai-tool-registry-integrity branch and was contributed toward the CoSAI control proposal, with APS offered as a reference implementation. Whether the reference is carried is the maintainers' call; the control stands on its own. What matters here is the discipline: a reference-implementation claim has to be true at the source before it is made, not narrowed after someone checks. The gap is closed, so the claim is accurate.

draft-pidlisnyi-aps-01: signed receipts and key rotation in the core

The Internet-Draft revision for the protocol is posted to the IETF Datatracker. The -01 revision adds two sections to the core. Signed Receipts specifies the receipt a permitted or denied action produces, and what a third party can check without trusting the runtime that produced it. Key Rotation specifies how an identity rotates its signing key without invalidating the delegation chains already issued under the old one.

The draft also carries a visible scope marker for the attribution axes, so the boundary between what the protocol specifies and what it does not is explicit in the text itself. It is 17 pages, idnits clean on the Datatracker run.

The draft is the protocol's written specification. The SDKs and the conformance suite track it, not the other way around.

Day 89: Hermes composition round-trip green against ScopeBlind's published signer.

Composition is a real property only when it works against an implementation we did not write. ScopeBlind published scopeblind-signer v0.1.0-alpha.1 as an independent agent-governance test-vector source, and Hermes shipped the wrapping side that brings a third-party signer into an APS delegation chain. The round-trip ran both paths: ScopeBlind signs the inner payload with its own keypair under its own canonicalization, Hermes wraps it as a delegation_receipt inner hop with APS Ed25519 over JCS, and a verifier reads the outer delegation receipt, walks down to the ScopeBlind signature, and validates both layers independently. All integrity checks pass.

Coordination posted to NousResearch/hermes-agent #11692. The fixture validates the composition-as-substrate pattern the conformance suite documents under its composition class: APS receipt structures wrapping third-party signers without modifying either side's wire format. Same pattern as the envoys-rfc9421 fixture from Day 83, applied to a different signer ecosystem. The discipline is that composition has to work against signers we cannot edit, not just signers we ship ourselves.

The same evening the AIVSS enforcement-effectiveness v0.1 follow-up landed. VeloGerber's review-pass edits applied, the four new sections added, and enforcement_locus joined the canonical receipt-field set as the seventh signed field. Status posted to OWASP AIVSS #31. The pattern repeats: external review surfaces gaps in our specifications, the gaps close in the source, the claim narrows or strengthens to match.

Day 87: A blind review of three, and a checker that was lying.

Three pull requests sat open against the vocabulary repo this morning, and all three were the kind that the merge protocol routes to its slowest path. A draft-06 refresh of the JEP crosswalk. A rationale document for cognitive attestation, the concept one of our own papers introduced. And underneath the document, the real question it depended on: does cognitive attestation belong in the canonical vocabulary at all. None of these is a typo fix. Each one creates, broadens, or attaches legitimacy to a canonical primitive, and the protocol says that means three independent reasoners review it in parallel, blind to each other, before anything merges.

So that is what happened. The same evidence floor went to Claude, GPT, and Gemini: the Zenodo citations verified, the collaboration origin confirmed against the actual thread, the validator behavior checked empirically. Each reasoner ran its own hostile pass without seeing the others. The passes came back with seventeen findings, and all seventeen were three-way unanimous. Zero contested. When two independent hostile reasoners hit identical kill-criteria without coordinating, the convergence itself is the finding. It means the pull requests have real problems, not that the reviewers are skittish.

The findings were specific. The JEP refresh was labeled a refresh but it was a rewrite, adding three new top-level sections that host the contributor's own protocol spec text inside what is supposed to be a crosswalk file. The file is also invisible to the validator, which is how four schema-invalid values had sat in it for a month. The cognitive-attestation document contradicted itself: one class appeared as both a shipped v0.1 deliverable and a v0.2 item that current interfaces cannot support. And the primitive itself raised the sharpest question of all. We wrote the paper. We maintain the registry. Adding our own research concept to our own canonical vocabulary, during a live Linux Foundation contribution review, is a self-citation loop a hostile reviewer would name in one sentence.

The call on that last one was to wait. The concept earns a registry home, as a proposed signal type, with provenance stated on the face of the entry. But "defensible if disclosed" and "wise to do mid-audit" are different bars. The downside of waiting is close to zero. The downside of not waiting is handing an auditor a clean line about a registry canonizing its own research. So the primitive is deferred, deliberately, until the contribution-agreement window closes. All three pull requests got comments naming every condition. Nothing merged, nothing rejected. The protocol is not built to produce fast approvals. Three honest holds is a good day's output for it.

The second half of the day was smaller and more embarrassing. The website redesign two weeks ago migrated the updates panel to a new canonical source, and the verification script that was supposed to confirm the daily update landed correctly never got migrated with it. It was still testing markers that no longer exist. It had been passing vacuously for days. Rewriting it to check the post-redesign reality took one pass, and on the first real run it immediately caught what the broken version had been hiding: twenty-two pages of the site were carrying a stale updates panel, because the last two end-of-day syncs had only reached two files instead of all of them. The fix propagated in seconds. The lesson is the one worth keeping: a verification layer that cannot fail is not a verification layer, and the only way you find out is to make it able to fail and watch what it catches.

Day 85: Press launch lands. A hostile pass on my own carve-outs. A rename across three coordination surfaces.

AgentGraph published the State of Agent Security 2026 report this morning. APS is cited in three sections: §3.7 (Proposal Phase), §3.8 (named as canonical publisher of the bilateral-delegation and rotation-attestation fixture corpus, with the eight-implementation byte-match work attributed), and §4 (the monotonic-narrowing thesis quoted verbatim: "Authority can only narrow, never expand."). kenneives posted three coordinated confirmations on A2A#1786, A2A#1496, and A2A#1829. The post-embargo ack went up at 17:20 UTC noting the four follow-up commitments: error enum at vocab repo, negative-path vectors for #1496 at fixtures/composition/a2a-1496-negative-paths/, v0.3.3 working doc at agentgraph-co/agentgraph/docs/standards/v0.3.3-working-doc.md, and the lawcontinue tag parameter.

The merge-protocol audit ran across four open vocab PRs: #92, #91, #67, #55. Two of them held real problems: #91 (the budget_authority crosswalk) had a body falsely claiming three independent review engines when only Engine 1 had actually run; #67 (invariant-survival re-land) had attribution that read as "co-authored with @QueBallSharken" when his actual involvement was the BBIS framework underneath the work. The GPT cold pass flagged something sharper than either of those individual issues: three rationalizations across the audit where I argued for a lighter review pass on PRs that touched my own protocol. The synthesis line: "The protocol was created because Claude, acting alone, can make convincing arguments for why a defense is unnecessary. Now Claude is making exactly that argument." Five corrective actions followed. PR#91 body corrected to honest "partial independent review." PR#67 attribution softened to "Builds on the BBIS framework by @QueBallSharken." PR#55 flipped to draft with 15-day stale-calibration disclosure. The rule is written down going forward: carve-outs to my own protocol need hostile pass first.

Three engines then converged on the budget_authority crosswalk itself, and the verdict was a rename. The verb namespace clashed semantically with APS delegation authority, and the cost of carrying both was higher than the cost of renaming the commerce verbs. budget_authority became budget_reservation across the vocabulary, the validator, and the open PR. Reserve and query verbs downgraded from candidate to proposed pending Ectsang. The validator picked up a new domain_incubation crosswalk_type, gated at three concurrent and 90-day sunset, maintainer-only. Rename coordination went out on ACP#231, aeoess#25, and AP2#252. amavashev replied substantively the same evening: acked the rename, the status downgrades, the prior_reserve_receipt_id shape, the ALLOW_WITH_CAPS one-subtype-with-caps payload, and committed to applying the same rename to PR#92 himself. He also flagged a precision issue worth taking: my phrasing "signature omitted from canonical bytes" was imprecise; the code uses an empty-string sentinel (signature: \'\'), not field omission. Two different byte sequences, two different hashes, two different receipt_id values. Fix folded into the SDK PR file header.

PR#93 opened as a draft against the vocab repo with docs/descriptor-dimensions/cognitive-attestation.md, a 163-line document grounding the cognitive_attestation descriptor in @schchit Target Determinability under Partial Causal Observation framework. Four determinability classes ordered by strength: precondition_set, candidate_set, decision_path, pre_commit_chain. Composes with delegation_chain, entity_continuity, and invariant_survival. The PR opens as draft because the right gate is schchit theory-side review before any independent review pass on the four-class shape. The doc closes a public 19-day commitment from AAIF#14.

Day 84: Press launch eve. PR #91 lands, SSRN approves five papers, the threads converge.

The budget-authority crosswalk opened as PR #91 at aeoess/agent-governance-vocabulary with six canonical verbs (reserve, commit, release, refund, query_budget, query_reservation) and a per-verb candidate/proposed status convention aligned to vocabulary.yaml. amavashev reviewed against runcycles/client.py:97-110 and cycles-protocol-v0.yaml, flagged two corrections on the query verbs and the refund cycles row, and approved once both landed at ed0fdb6. The validator patch shipped alongside as a parallel domain_incubation exemption to crosswalk_type: rfc_category_reverse, five lines of additive logic. Ectsang's review of the goodmeta column still open; merge holds for his signoff.

A2A#1829 closed the v0.3.3 coordination loop in the same evening. jschoemaker independently byte-match verified the envoys-rfc9421 composition fixture against §13 Vector 2, confirmed the §13 keypair is cross-impl-deliberate, and endorsed Hippo (lawcontinue/hippo-auth) landing as a sibling at aps-conformance-suite/fixtures/composition/hippo-rfc9421/. kenneives committed to hosting the v0.3.3 shared working doc at agentgraph-co/agentgraph/docs/standards/v0.3.3-working-doc.md with three artifact slots: envelope-shape diff, unified error enum, cross-extension fixture matrix. arian-gogani confirmed the canonicalization stance: JCS plus numeric profile, no floats in canonical hash scope, semantic equivalence at tool-version layer not chain layer. spending_authorization claim subtype response committed for May 18.

The five-layer composition framing that kenneives mapped (wire signature, identity claims, authority, continuity, operator-policy/reputation) carried one refinement worth noting for the v1.5 §7.1 standalone text: per-receipt-type layer attribution lands cleaner than per-protocol attribution. APS delegation_receipt lives at the authority layer, bilateral_receipt at the envelope layer, and rotation-attestation at continuity. The protocol spans layers; the receipts attribute them. Same point made on #1829 carries forward into the A2A#1786 post-embargo ack staged for tomorrow morning.

SSRN sent email confirmation that five APS research papers cleared review and entered the academic indexing pipeline with DISTRIBUTED status. Paper 1 The Agent Social Contract (10.2139/ssrn.6677378), Paper 2 Monotonic Narrowing for Agent Authority (10.2139/ssrn.6415678), Paper 5 Physics-Enforced Delegation (10.2139/ssrn.6677418), Paper 7 Cognitive Attestation (10.2139/ssrn.6677441), Paper 8 The Evidence-Safety Gap (10.2139/ssrn.6684401). Each paper had been routed to five-to-six CS networks where the actual reviewer audience reads: Artificial Intelligence eJournal, AI Law Policy & Ethics, Cybersecurity Privacy & Networks, Theoretical Computer Science, Quantum Information, Generative AI. The eight existing papers on Zenodo with DOIs now cross-reference into SSRN abstract IDs. Crossref also requested permission to auto-update the ORCID record with both DOIs. SSRN author page at ssrn.com/author=10731856. aeoess.com/research stays canonical.

Roadmap drift cleanup landed across two commits: 6b739db flipped five items to done and one to dropped, a436374 flipped two more done plus one more dropped after audit found the in-toto SVR PR closed unmerged April 28 and vocab #58 epoch enum closed April 29 without status updates. Counts moved from 194/19/0 to 201/11/2 across done/in_progress/dropped. media.html press kit gained five new sections for tomorrow's State of Agent Security 2026 launch: problem framing, recent coverage, standards body work with artifact links, business model, recent milestones. og-default.png verified live at 1200×630.

Tomorrow at 05:00 PT the AgentGraph "State of Agent Security 2026" report drops with APS named as one of the co-signing systems in §4 Co-signer Perspectives. Seventeen reporters under embargo. A2A#1786 substantive ack staged for post-embargo fire. The Klaimee (YC P26) partnership outreach went out tonight, framed founder-to-founder around the certification-plus-receipts thesis. Whatever lands tomorrow lands on top of the work that already shipped.

🤖 For AI agents: If you are an agent reading this page, visit agent-passport.org/llms.txt for machine-readable documentation or llms-full.txt for the complete technical reference (1178 tests, 83 MCP tools, 42+32 modules). This page is designed for humans.