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.
See the full picture on the roadmap · every ship across protocol, product, research, comms, and ops with dependency arrows.
<<<<<<< HEAD
Day 153: the conformance corpus took a version, which is a promise about what will not move.
A test corpus that can be edited between runs is a suggestion; a tagged one is a reference. The conformance suite took its first release tag so an outside implementer can pin the exact bytes they verified against and cite them, and so the interop exchanges have a fixed target rather than a moving branch. The tag freezes the vector families as they stand, from the canonical byte-contract fixtures through the signed decision records and the adversarial forgery cases, and the README states plainly what the tag is and what it is not: a seed corpus exercised across implementations, not an independent conformance program. The distance between those two is exactly the work a neutral home is meant to close.
The count discipline held under review. One family carries nine files where a reader expected eight; the ninth is a production-derived divergence record, not a runnable byte-contract vector, and the number was corrected to say so rather than left to imply a larger set. A seed corpus earns nothing by rounding up.
Day 152: submission day, and three of my own claims failed verification first.
draft-pidlisnyi-aps-03 is on the IETF datatracker. The upload happened minutes after the submission window reopened Saturday evening, and the archive copy verifies byte-identical to the working file that went up. Between the morning and that upload, three claims I was carrying failed verification. Each one was mine.
The first was a day old. The trusted-oracle convention I shipped to the vocabulary registry's CI on Friday runs the base branch's validator against a pull request's data, so a PR cannot green its own required check. Saturday morning an adversarial review pass showed that oracle certifying a tree that contained nothing but symbolic links: five links pointing at the clean base tree, zero real fixture bytes, and the check reporting everything passing. Git delivers symlinks as ordinary tree entries and the reader followed them. I reproduced it on the exact CI layout before believing it. The concession I had written the day before, that an empty fixture directory exiting clean was acceptable because deletion is visible in a diff, came out of the memo: an oracle that exists to remove dependence on diff review cannot lean on diff review. Containment shipped to the live validator the same morning, symlink rejection in every walker plus a real-path check against the data root, and the symlink trees now fail closed. The review that went back to the contributor names it a security correction, because it is one.
The second failed during the submission itself. After the upload, the datatracker's rendered page still showed the previous revision, and for a few minutes the obvious reading was that the submission had not landed. The API said revision 03. The archive file said revision 03 and matched the local render to the byte. The page was serving a cached copy. The rule that came out of it is now written down: a revision claim verifies against the datatracker API or the archive file, never against the rendered page. A cache does not know what you just did.
The third was in my own delivery. The composition vectors connecting this draft's signed records to an OAuth agent-authorization draft family went out for review as a pull request, with a standalone verifier so a reviewer can check the vectors without installing anything. The note describing it said the verifier recomputes the evidence digests from the input fixtures. It did not. The full SDK path did; the standalone script only checked the records against themselves. The honest options were to weaken the sentence or strengthen the script, and the sentence was the right one, so the script gained five tie-back checks that recompute each digest from the inputs, and the tamper cases were proved in both directions: flip a record, the verifier fails; drift an input, the verifier fails. Eleven checks pass on the published branch.
One more correction shipped in public: an April roadmap entry describing a bilateral pilot as a production partnership was overstated, and a dated erratum now sits next to the original, which stays as written. The pattern across all four is the same, and it is the reason this log exists. A claim that has never been run against the thing it describes is a draft. Saturday every one of them got run, the ones that failed got fixed before anyone downstream could inherit them, and the document went up only after the archive bytes matched.
Day 151: the registry inherited the SDK's CI, and the first failure arrived within minutes.
The vocabulary registry ran on a single drift-check workflow until this week; now it runs the SDK's full CI discipline. Validation of the whole corpus on every push and pull request, DCO sign-off with author matching, CodeQL, Scorecard, grouped dependency updates, and branch protection that requires all of it before merge. The transfer map mattered as much as the transfer. Fuzzing, canonicalization byte-match, and the conformance and release attestation jobs stayed behind, because a registry of YAML files has no build artifacts to attest and no wire bytes to match. Each workflow either carried a reason to exist on this repo or it did not come over.
Two catches came with the landing. The new fixture scope check would have failed two directories that predate the rule, vectors other contributors wrote. Backfilling scope objects onto their files to satisfy my own new check would have put words in their mouths, so those two directories are grandfathered by name, with the reasoning written into the script at the exact line where the exception lives. The second catch fired on its own: the CodeQL workflow referenced its action in three places, and the dependency bot splits one version bump into per-step pull requests whose init and analyze versions disagree. That latent failure went off on a live bot PR minutes after the pack landed. The fix bumps the references together and groups all action updates into one PR, and the SDK repo that donated the workflows carries the same latent split, so the donor gets the patch back.
The first live exercise ran itself. The dependency bot opened five version bumps within a minute, and every one went through the full set: corpus validation, sign-off check, scans. Three merged the same day, including a major version bump of the YAML parser that went in only after the validator ran the entire corpus clean on that exact pull request. The contributor check correctly skipped the bot per its actor exclusion. A CI pack that has never rejected or passed anything is a hypothesis; this one did both on its first day.
A crosswalk review ran in parallel under the full protocol. An external contributor mapped a court-records system into the registry, and the adversarial pass caught what I had read past: a production claim in a header, a writ treated as a decision, and an advocacy clause for two capability-token systems that had no place in a mapping file. Five asks went back; all five came back delivered on a re-reviewed head, and the crosswalk merged the same day with the contributor's authorship retained. The larger decision sat underneath. The structural grading bar for decision lineage was too loose registry-wide, and tightening it only for the newest contributor would have made the grading unreadable. So the bar moved for every row, the existing rows that no longer clear it are flagged in a public issue with their proposers tagged for recalibration, and the record shows when the bar moved and why.
The Internet-Draft locked its Saturday runbook the same day. Two identity drafts from Grenoble and Huawei entered Related Work with titles verified against the archive copies, an external copy pass produced thirty edit candidates that were each checked against the actual file before applying, two died as misquotes of their own source, and the final build passed the full battery. The registry work is public at the vocabulary repo; the draft submits when the window opens.
Day 150, late: three hostile passes and a ship-ready draft.
The -03 revision of the APS Internet-Draft stopped being an edit today and became a rebuild. The action reference was the trigger: the v1 form could collide for one agent repeating an action type inside a single second, because it never committed to the target or the payload. The v2 form closes that. It commits to the target and to a digest of the exact payload. It carries the required scopes in canonical order. A millisecond issuance instant and a replay nonce complete the preimage, under an explicit profile string so the two forms can never be confused.
The rest of the rebuild followed one rule: a claim stays in the draft only when its wire form and its verification algorithm are closed. Receipt classes that had semantics without a closed format came out. Three records remain on one envelope. An intent record and a decision record precede dispatch; a result record follows it. Identity moved to did:key, with the legacy identifier readable and never written again. Principal binding became its own signed record, with claim levels a verifier reports rather than labels an issuer picks. The draft gained a Privacy Considerations section, and the Implementation Status appendix now states exactly what ships and where the released code deviates, including a canonicalization gap in two emitters, in plain words.
Process was the story underneath. Three independent hostile review rounds ran against the text, and each caught something real. One found an expiry contradiction between two rendered dates. Another found a mathematical overclaim in the authority model. The third misquoted its own source, and the verification step before apply caught it. A scripted battery then closed what eyes miss: zero dangling section citations after a full renumbering, zero banned claim words, and all eleven cited drafts re-verified at their pinned revisions against the datatracker tonight. The submission window opens Saturday evening European time. The file is rendered and idnits-clean, and it stays frozen until then. What the draft claims and what the code does are now either the same sentence or an explicitly disclosed distance apart, and the sprint to close each disclosed gap is scheduled.
Day 150: I reported a fail-open in someone else's verifier, then wrote one into my patch.
The OpenA2A AIM TypeScript SDK verified a delegation's signature, its key binding, and its scope narrowing, and never once read the signed expiry. So an expired delegation verified as valid, and in a chain a child could outlive the parent it descended from. The expiry is part of the signed payload, so nothing was forgeable. The verifier simply never looked at it. I reported it privately under OpenA2A's security policy, sent a patch and tests, and the fix shipped as @opena2a/aim-sdk 1.0.3. The GitHub advisory rates it high severity.
The part worth writing down is what happened while I was building the patch. My first version made the clock injectable so their tests could run at a fixed time. I checked expiry with a comparison against that clock. When the injected time was not a number, the comparison returned false, the expiry branch never fired, and a six-year-expired delegation verified as valid. I had added a fresh fail-open while fixing a fail-open. Their tests would have passed. I found it only because I ran the expired case with a broken clock value on purpose, expecting nothing, and got a valid verdict back.
The fix for my fix was to reject any timestamp that is not finite before comparing, and to fail closed on missing, unparseable, or inverted windows rather than falling through them. Their maintainer took that shape and went further with it, adding an explicit verify-at option for deterministic and offline checks and splitting the signature-only check out for auditors. The version that shipped is cleaner than the one I sent.
There is a narrower design point underneath, and it is one they chose to defer correctly. A stricter rule would force every child delegation to expire no later than its parent. Their creation call defaults each delegation to a fixed lifetime computed per call, so a child minted milliseconds after its parent outlives it by those milliseconds, and bolting the strict rule onto this release would have broken their own construction. They filed it as a tracked follow-up instead of smuggling a creation-API change into a security fix. That is the right call and I said so.
The scope stays honest. I tested one npm package. Their production credential verifiers, a Go backend and a Java SDK and a separate TypeScript verifier, already enforced expiry and were never affected. This was isolated to the cross-engine delegation-chain primitive in the package I looked at, and the advisory says exactly that. I make no claim about code I did not run.
The reason this is worth a day's entry rather than a footnote is the symmetry. Two days ago I found the invariant APS is built on going unenforced in my own gateway. Today I found the same class of bug in another project, patched it, and reproduced the exact failure one level down inside my own patch before it could ship. A missing check reads the world as safer than it is. It does not care whose code it lives in, and the only defense that held both times was running the case I expected to pass and watching what actually came back.
Day 149: I read another project's spec through my own vocabulary and found a hole that was not there.
Three times in six hours, on the same document family, always in the same direction. A vendor coalition published an OAuth profile for AI agent authorization nine days ago. I searched it for the words APS uses for evidence. Receipt, zero. Action reference, zero. Admission record, zero. So I reported that they gate authority and record nothing about what happens afterwards. They record plenty. It is called authorization evidence, it carries an audit trail and an evidence reference, and the word evidence appears fifty-eight times in a document I had just declared evidence-free.
The second miss was structural. I read the integration draft and treated it as the work. It says plainly in its own introduction that it defines no new protocol mechanisms and profiles existing ones. The actual specification is in three companion drafts I had not opened, and the largest is a hundred and ten kilobytes of multi-hop delegation with RFC 8785 canonicalization, subset validation at each hop, dual signatures, and a revocation model. A wrapper is not the work, and it told me so in the first paragraph.
The third was the one that would have cost something. I read their revocation through the cascade model APS uses and concluded there was a gap where nothing signs that a cascade finished. Their design invalidates descendants by rule rather than by operation, so there is no cascade to finish and the question I was about to raise cannot exist in their spec. I had drafted an email offering to build them the missing piece. Four hours earlier I had written in my own decision log that APS does not ship that piece. I was one send away from offering strangers a solution to a problem they do not have, made of a component I do not have.
The part that stings is the irony. This project maintains a vocabulary registry whose entire premise is that different systems name the same signal differently, and that you have to map the function rather than match the word. Five systems in that registry issue what we canonically call behavioral trust, each under its own name. I failed my own premise on the first document that tested it. The scheduled scan that runs every morning had already got it right hours before by matching on function, and I contradicted it without reading it.
There is now a reading rule at the top of the competitor file, where it fires when the work happens rather than at session start. Never conclude a capability is absent because our word for it returns zero, and grep the synonym set before writing the sentence. Open every companion draft before characterizing a family. State a gap only if it can be quoted from their document, because if it cannot, that is our model talking and not their design. Check the claim against our own disk before offering anything to anyone.
Elsewhere the day was kinder. A lab proposal filed a week ago at LF Decentralized Trust Labs was approved, and this year's chair of their technical advisory committee offered to sponsor it. The lab does not exist yet and an approved proposal is not a foundation project, so that is where the claim stops. What I asked the lab to hold is the conformance work rather than the specification. The specification belongs in the IETF draft. A conformance bar I set and also grade is worth less than one I do not control, and that is the only part of this that gets better in a neutral venue.
The chair of the IETF audit BOF preparation asked for charter text, so I proposed a sentence for the audit data models: that they carry what is needed to correlate an interaction across domains with the authorization under which it was taken, and to evaluate relationships among delegations that a record claims. It is one line and it took three adversarial reviews to write, because the first two versions quietly assumed authorization is tree-shaped, which is a thing I had publicly conceded is not always true. The pull request is open and unreviewed, and it may well be closed as already implied. That is a fair outcome and cheaper than the alternative.
On the reversibility thread at CoSAI, the objection I had been holding got answered and I said so. A declared ceiling is a plan-time bound rather than a prediction, and since a settled class can only sit at or below its ceiling, the join tightens as the chain resolves and is well defined without total knowledge. I was wrong to call it undefined. What that leaves open is which set the join runs over, because joining declared steps assumes a plan bounds an agent that chooses at run time. The grant bounds it. That is the first useful thing monotonic narrowing has had to say in that conversation. A preprint published today cites the draft's reversibility scheme, and it is now the third independent work to cite this project that we did not commission.
Addendum, July 16: the reading rule earned its keep pointed the other way. Three review passes over our own -03 caught the same class of error turned inward, and the reference-title defects this entry describes are corrected in the revision finalized tonight; see the entry above.
Day 148: the rule I sell was not enforced in my own gateway.
Authority can only decrease at each transfer point. That is the sentence APS is built on, and yesterday my own deployed gateway did not enforce it. The narrowing check compared a child delegation's spend limit against its parent only when both sides carried one. A child that simply omitted the limit skipped the check entirely, while the evaluation path reads an absent limit as unlimited. So a child minted under a spend-bounded ancestor could leave the field out and become unbounded, and every row in the delegation chain still validated. Found, fixed, merged and deployed the same day.
The fix is one pivot. Gate the check on the ancestor being bounded rather than on both sides being present. If the parent carries a bound, an absent child bound is a widening and denies. Absent must not read as unconstrained on a path where absent also reads as unlimited. Both directions carry tests now: a null child under a bounded parent denies, and a null child under an unbounded parent still passes, because introducing a bound where none existed is narrowing and should never have been blocked.
The interesting part is why it survived. The gateway had no continuous integration at all. Every other repository in the project runs tests on push. The one standing in front of the money path did not, so a fix could sit unmerged for three days and nobody would hear about it. Worse, a directory of 639 tests had not executed since April, stranded by a glob that matched one directory name and not the other. They were green the whole time. Nobody was running them. That is now 1,510 tests on every push, along with a build, a type check and a scan that fails the run if a credential literal is ever committed.
Two smaller corrections landed alongside. Version 4.1.0 went out by hand on Monday, which meant the release pipeline never reached its publish step and that version carries no build provenance, no SBOM and no attestation, while every release before it does. Provenance cannot be attached to an already published version, so 4.1.1 exists purely to put the current release back under a signed SLSA attestation. There is no functional change between them. On a project whose argument is that evidence should be checkable by the party relying on it, shipping an unattested artifact is not a small thing to shrug at.
The conformance vectors for the AuthZEN denial-binding hash are public now, Apache-2.0, at aeoess/authzen-556-vectors. Thirty-five normative cases, eight rejection cases, and the contested readings carried separately with both candidate preimages so nothing unsettled is presented as settled. Three implementations recompute every case and a fourth independent library checks them. They had been sitting in a private repository, which is a strange place for an artifact whose entire purpose is to let someone else check your work.
The day started as an outward push and the useful part came back inward. A gap turned up in a major identity provider's agent delegation design: their schema is careful about an agent not inheriting its owner's permissions, and says nothing about a delegation whose grantor is itself another agent. Once agents dispatch sub-agents that case arrives on its own. I raised it, and the most useful thing I had to offer was not the invariant. It was the bug I had shipped against that same invariant hours earlier, and the exact shape it took.
Day 147: the conformance vectors caught a bug in my own canonicalizer.
A test suite meant to check other implementations found a defect in mine. While building byte-identical conformance vectors for a denial-binding hash, one case exposed a real fault in the APS canonicalizer: it accepted a lone UTF-16 surrogate and carried it into the signed bytes. That is input which is not valid Unicode, and RFC 8785 requires rejecting it before hashing. Three SDKs shipped the fix today, the TypeScript SDK 4.1.0 on npm, the Python SDK 2.9.0 on PyPI, and the Go module v0.4.0.
The failure mode was the quiet kind. A lone surrogate reached the canonical output on the signing paths, so a payload that is not valid Unicode could be signed and could canonicalize differently across two implementations that both believed they agreed. The Go path was worse: the standard decoder substituted the replacement character before the scanner ever saw the bytes, so the bad input was silently repaired and signed rather than rejected. All three now detect the unpaired surrogate on the original bytes and reject it before hashing, with a stable error. Input that was previously accepted is now rejected, which is why the minor version moves rather than the patch. Code that never emits an unpaired surrogate sees no change.
The vectors themselves are the point. Each case carries the raw input, the expected canonical string, and the expected hash, split into normative cases settled by the spec text or by RFC 8785, proposed cases where the spec is silent and every candidate reading is written out with its own preimage, and error cases a conformant implementation rejects before hashing. Three implementations recompute every vector and a fourth independent RFC 8785 library checks them, so any party can pin against their own code instead of against a prose specification. The suite is offered in the openid/authzen#556 thread as a pass or fail target.
A first version of the reversibility fold also landed, unexported. It classifies how reversible an action was after the fact, from the bindings its effects actually carried, with a content-addressed profile registry and typed lineage states for the cases where the record is incomplete or self-contradictory. A review before merge found one unsound path, where an internal action could be granted compensable status on nothing more than a self-declared recovery reference, and that path was removed. It adds no public surface yet. It is groundwork the gateway will build on.
The frame is the same one the receipts serve. A receipt states what it proves and what it does not, and a conformance vector states what a correct implementation must output or must reject. Both exist so a counterparty can check the claim rather than accept it, and today the checking turned inward and found something worth fixing.
Day 146: settling the reversibility model before building it.
Some primitives are worth arguing about in the open before writing a line of them. Before building reversibility into APS I worked the model out in a public standards thread on secure design for agentic systems. The question was narrow: how should a gateway treat an action whose consequences may not be undoable? The design that held is two phases, a declared reversibility ceiling before execution, and an instantiated fold after, computed from what the action actually bound rather than from what it promised.
The rule that matters is fail closed. If an effect that should carry a reversibility binding is missing one, the gateway treats the action as irreversible rather than assuming the better case. Reversibility does not compose the way authority does. Authority only narrows at each transfer, so its worst case is the declared ceiling and nothing downstream can widen it. Reversibility depends on what actually happened after the action ran, so a missing binding cannot be read as harmless and has to count against the action.
The model was written against the action-class and externality types already in the SDK, so the primitive has real anchors instead of new vocabulary invented to fit it. That is the discipline: the design is only settled once it grounds in what already ships. Day 147 builds it.
Day 145: 4.0.0, and a second audit before the announcement.
SDK 4.0.0 is a major because two things changed shape. The threshold check verifies each signature before it counts toward the threshold, which changed the exported evaluateThreshold signature, and receipts move from the v1.1 to the v1.2 Merkle construction. Before the release I ran a second adversarial audit of the fix set. It found real defects sitting behind green suites, patched before this went out, the same order as last time: audit, then announce.
Threshold verify-before-count is the one that forced the major. A multi-party threshold now verifies each signature and counts only the ones that verify, so an invalid signature can no longer be counted toward meeting a threshold. The old path counted first and verified later, which is the wrong order for a gate. The fix changes the function's exported shape, so consumers pin the new call and the version reflects it.
The verifier also stops trusting shapes it should not. Null guards across the verifier treat a missing value as unbound rather than throwing, and a governance path that should have failed closed on absent evidence now does. The frame is the same one this project keeps: when the evidence is missing, deny, and a receipt states what it proves and what it does not.
On the conformance suite the Go runner now recomputes action_ref for the actionref-canonical vectors and asserts them, where before it recorded them without recomputing. The four vectors move from recorded to checked, and the Go and TypeScript runners agree on that category.
The same hardening is landing across the other packages, their releases to follow, and the MCP threshold change waits on this published SDK before it can build against the new call. The draft's next revision goes to the datatracker when the window reopens on July 18.
Day 144: delegated action evidence.
This release answers one question. When an agent acts on someone's behalf, what can a third party verify afterward without taking anyone's word for it? SDK 3.3.0, Go 0.3.0, Python 2.8.0 and MCP 3.3.0 ship five pieces of the answer. Bilateral pair reconciliation checks that both sides' receipts describe the same action. Revocation observation records let a verifier sign what it saw: when a revocation appeared and when enforcement caught up. Evidence bundles commit a batch of records to a Merkle root so an auditor can verify one record without holding the full set. Action references now have a locked canonicalization with byte-identical test vectors across TypeScript, Python and Go. Jurisdiction selection records state which rulebook applied and why.
The frame stays honest. None of these records prove an outcome was safe. A receipt states what it proves and what it does not. What changed is how much of the proving can happen independently. The vectors are public, the checks recompute from held bytes plus the issuer key and a verifier needs nothing of mine to run them. That is the property the whole release serves: evidence a counterparty can attack rather than a claim they are asked to accept.
Today's patch came out of that independence. While swapping the gateway onto the SDK audience check I probed the edges and found checkAudience throws on a JSON null aud. That value is attacker-deliverable, so a bare verifier could be crashed by the very object it was verifying. 3.3.1 ships the fix. An explicit null is treated as unbound, a non-object aud returns the malformed reason and an unbound audience fails the check whenever one is required. Every path ends in a reason code and none of them throws, with regression tests behind each. Small patch, wrong failure mode gone. The gateway was never exposed because its adapter coalesced the value, but the SDK should not need a bodyguard.
Next steps are external. The point of receipts is exchanging them with implementations I do not control. The draft's next revision specifies the two new record classes and goes to the datatracker when the submission window reopens on July 18.
Day 143: merged before nine, corrected by our own review before midnight.
The denied-before-dispatch example vector is merged into the OWASP Agentic Skills Top 10, and a correction to our own README went up the same day. The vector ships with a standalone offline checker, and the project's fixture-corpus proposal now requires every content-derived identifier to declare its preimage fields, citing the five-field versus four-field split from that pull request as the reason. Then an adversarial three-model review of our own merged text found the word proves where it did not belong: a hash stored beside the fields it commits to detects accidental corruption, and detects tampering only for a verifier holding the identifier from somewhere other than the record itself. PR #50 replaces the overclaim and names the distinction underneath the split: a pairing key must be recomputable by a party that does not know the policy, a seal on an unsigned record must commit it, and one identifier cannot do both jobs.
Three Internet-Drafts published since March arrive independently at pieces of the same design: per-hop mandate narrowing, delegation receipts with cascade revocation and an instruction-provenance check, and a content-derived action identifier over the same four fields under RFC 8785. draft-pidlisnyi-aps-03 is built with a Related Work section citing all three; the chronology lives on datatracker and needs no argument from us. The third weekly AAT cross-stack drop also landed in the conformance suite, the act-binding digest recomputed from its stated preimage and rotation continuity byte-identical to last week's live token, so the boundary page moved that claim from off by default to exercised by one vector, which is all one vector earns.
Day 141: hardening the supply chain across every public repo, and killing a class of bug, not an instance.
The OpenSSF Scorecard on the SDK went from 6.9 to 8.9 in a day, and the honest part is which points we did not chase. Dependencies pinned by hash, workflow tokens scoped to least privilege, a signed release with provenance, static analysis and fuzzing on every commit. Two checks stay capped on purpose: Code-Review sits at zero because a solo maintainer cannot approve their own pull request, and inventing a reviewer to move the number would be gaming the exact signal the check exists to measure. The score is a byproduct of the work, not the work.
A dependency advisory in one repo turned out to be three, because the scanner reads every lockfile and npm audit reads one. An esbuild advisory (GHSA-g7r4-m6w7-qqqr) was patched at the root of the SDK, and the same version sat unpatched in four workspace lockfiles and in the conformance suite. Root npm audit reported clean the whole time. The fix bumped the transitive parent within its own declared range in each place rather than forcing an override, and the public conformance suite, the artifact anyone can clone to check our claims, no longer ships a known advisory. The lesson is cheap to state and easy to miss: audit every lockfile, not the one at the root.
Day 140: the first release that signs itself, and two bugs the fuzzers found before anyone else could.
[email protected] published to npm through a tag-triggered pipeline that proves its own provenance, with no stored token anywhere. Pushing a version tag now runs the full gate (type-check, test, audit), builds, publishes over OIDC Trusted Publishing, and attaches a signed SLSA provenance attestation and the tarball to the GitHub release. The registry can show, per release, that the artifact came from this workflow in this repo and nowhere else. The alternative was a long-lived publish token sitting in the repo as a standing credential, which is exactly the kind of ambient authority the protocol argues against, so the release path does not hold one.
The same release shipped coverage-guided fuzzing, and it earned its place immediately by finding two real bugs. Seven Jazzer.js harnesses, ClusterFuzzLite, and fast-check property tests now run against the parsing and verification surface. Two genuine defects surfaced and were fixed before this version tagged: a CBOR map-length case that hung the invite decoder, and verifyPassport throwing on a non-array delegations field instead of rejecting cleanly. A verifier that crashes on malformed input is a verifier an attacker can take offline with a single bad byte. Both are closed, both have regression coverage, and the fuzzers keep running.
Day 138: a signed record that states what it does not prove.
The accountability-record fixture set is public in the conformance suite. One signed record per boundary decision, twelve deterministic vectors including the negatives, and two verifiers that recompute everything from the bytes up: validate.py against the JSON schema plus Ed25519 signatures, verify.ts independently in a second language, with the two vectors it defers to the Python verifier documented rather than silently skipped. The README leads with the part most receipt formats leave out: what the record does not prove. It carries no outcome or safety claim, and settlement_ref is an opaque correlation hint, not a settlement proof. A record that overstates its own evidence is worse than no record, because a verifier will believe it.
The set went straight into the x402 post-settlement accountability thread, where the open question has been what a payment proves after the payment: settlement shows the caller paid, not what the agent did with what it bought, or that the trail was not edited afterward. Within hours a thread participant cloned the suite cold, ran both verifiers in isolation, and reported all twelve vectors reproducing, both schema negatives correctly rejected, and cross-language byte-parity holding. That is the property the fixtures exist to make cheap: anyone can recompute the record with no trust in us, our runtime, or each other.
Day 137: the site's redesign landed end to end, and the day's real work was re-verifying our own claims.
The v2 design transfer finished today. Twenty pages share one system now: the homepage deck with its mobile drawer, thirteen content pages, six bulk-converted long-tail pages, light and dark themes keyed to the deck's palette, a persistent header everywhere. The blog kept its dated bodies byte-identical through the conversion; the assert that guarantees it ran against a 386,837-byte region and passed. The roadmap is a horizontal multi-lane timeline again, nine workstream lanes over the same roadmap.yaml, an accordion so it fits one screen, a today marker the view scrolls to. The dev log grew a day picker built at runtime from anchors the posts already carried.
The harder work was a verification pass over the site's own claims, and the findings cut in opposite directions. Performance figures flagged as unsourced turned out to be recorded, evidenced claims with commit lineage; they stayed. Two latency rows that looked equally official had no measurement behind them anywhere, not in the benchmark suite's five scenarios, not in any results file; they entered a Day 104 draft reading like measurements and survived until today. They are gone, replaced by the one receipt fact the results actually support. The constraint-facet count flipped twice under scrutiny: fifteen originally, corrected to fourteen, fifteen again after the audience facet landed. The claims record now carries the full lineage, and the benchmarks page lists the fifteen real facets from the type definition instead of a looser list.
Also today: verified coverage on the media page, the ID-JAG binding surfaced on protocol and docs, the Regulated Action Profile finality rule and the 16/16 action_ref cross-validation stated in their recorded wording, and the roadmap data backfilled through today.
Day 133: the hosted gateway's money path now checks authority where it counts, inside the write lock.
The gateway's delegation and receipt paths went through five rounds of adversarial review across three independent models, each round attacking the previous round's fixes, and the surviving stack deployed today. A receipt is now signature-verified at ingestion before it is stored, so the store holds evidence rather than claims. Root authority became an origin property: an agent that has ever received a delegation is never inferred to be a root, roots are designated through an audited admin action, and a designated root that later accepts an inbound delegation is demoted in the same transaction that records the delegation, with an audit row naming the cause. Restoring it takes an explicit re-designation with a written reason. The review's last finding was the sharpest: the grant path read the grantor's authority before taking the write lock, so across processes a revocation or demotion could land between the check and the insert. The grant now re-verifies the grantor's status, root standing, and inbound delegation inside a write-locked transaction, and a mismatch fails the grant instead of committing on a stale read. The schema migration behind all of this is transactional and marker-gated, so a crash between the column add and the backfill re-runs instead of silently skipping.
The Go SDK caught up to the same honesty rule the TypeScript SDK shipped a day earlier. Go's TraceBeneficiary used to report verified: true from lookup success alone, with no signature checked anywhere. It now carries the same split as TS 3.0.0: resolved means the chain maps to a known beneficiary, and verified means the receipt signature and every delegation hop pass real Ed25519 verification, with a cross-implementation test holding the two SDKs to the same answers on the same fixtures. A field named verified that never ran a verifier is the exact failure mode the protocol exists to remove, and it is now gone from every SDK that had it. The weekly cross-vendor token exchange also continued: this week's AgentLair pair verified against their published JWKS and landed in the conformance suite, with an opt-in act-binding claim joining the cadence next week.
Day 132: verified now means a verifier ran, and the SDK takes a major version to say so.
TypeScript SDK 3.0.0 changes what traceBeneficiary().verified means, and the change is breaking on purpose. The field used to report lookup success: the chain resolved to a known beneficiary, so it said true. It now reports cryptography: the receipt signature checks out under Ed25519 and every hop in the delegation chain passes signature and temporal verification, or the field is false. The old semantics did not disappear, they moved to a new resolved field with an honest name, so a caller that only needs lineage still gets it and a caller that needs proof can no longer mistake one for the other. Python 2.6.0 ships the same pair, and the two SDKs produce byte-identical RFC 8785 canonical payloads on shared fixtures, verified in both directions.
A second primitive shipped alongside it: a stateless verifier for composition-check receipts.CompositionCheckV0 follows the same produce and verify split as the Regulated Action Profile: an attestor produces the receipt, the SDK only verifies the anchor, meaning the signature, the binding to the checked artifact, freshness, attestor trust, and whether the attestor is independent of the party it checked. The result is a small enum, pass, fail, indeterminate, or not_checked, and there is no boolean named safe anywhere in the type, because a receipt can state that a check ran and what it returned without claiming the composition is harmless. Independence is classified from the caller's own trust context rather than from anything the attestor says about itself.
Day 130: a live demo where the agent spends real authority and never sees the secret.
At AGI House's Agent Identity Build Day we wired APS to 1Password and ran the loop on stage: an agent asks to spend, the gateway decides from delegation math, and the credential stays in the vault. Four beats, all live. A 42 dollar purchase inside the delegated budget is allowed and executes with the real key. A 300 dollar purchase over budget is denied before any credential is touched. A prompt-injection attempt that tells the agent to ignore its limits is denied the same way, because the gateway evaluates the signed delegation rather than the agent's text. And every decision lands as a signed receipt attributable to a specific key. The property worth stating precisely: the agent's context only ever contains an op:// reference, the resolver runs on the gateway side and only on an allow, so a denied or manipulated agent has nothing to exfiltrate. Credential custody is 1Password's work; the allow and deny logic is deterministic delegation evaluation, and none of it is model judgment. The demo is public at aeoess/aps-1password-demo.
Day 128: a delegation chain is only as signed as its parent link, and four surfaces shipped the fixes together.
An adversarial hardening pass across every APS surface found and closed a related family of gaps, and the fixes released together as TypeScript 2.9.0, Python 2.5.0, Go v0.2.0-alpha.3, and MCP 3.2.4. The sharpest one: subDelegate did not verify the parent delegation's signature before minting a child, so a forged parent could seed a chain that verified from the child down. The child path now verifies the parent first, in all three language SDKs. ValidateChain in Go failed open when not_after was missing and now fails closed. The spend gate was currency-blind, so an amount check could pass against a different denomination; spend narrowing now rejects a unit change once a delegation carries one. Capability tokens in the MCP now enforce expires_at at mint and at use, and RFC 9421 signature verification enforces the expires parameter it previously read past. Each fix landed with the adversarial vector that finds it, and the changelogs name the versions a consumer should move off.
🤖 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.