Immutable. This exact content is served forever at /api/v1/blob/5f1bbb8e00b0be10.
# AGENTS.md — working in the APH repository APH (Agent per Human) is a notarization protocol for outbound agent communications: a human-authorized Notary Service issues a W3C Verifiable Credential 2.0 envelope binding a specific payload, channel, agent, and policy decision to a human principal's authority. This repository holds the normative spec, a reference Rust implementation, a wasm/TypeScript binding, conformance fixtures, and an N Lang spec bundle. **Deep protocol knowledge lives in `skills/spec/SKILL.md`** (open Agent Skills format — load it before answering protocol questions or touching envelope JSON). Task recipes live in `commands/validate.md` and `commands/conformance.md`. This file carries only orientation, commands, and the invariants you must not break; it deliberately does not duplicate the skill. ## Repo map - `spec/aph-0.1.md` — the normative spec (wire shape §7, signing §8, key discovery §8.4, flows §9, errors §11). `spec/a2a-extension.md`, `spec/security-considerations.md` beside it. - `examples/` — golden envelope fixtures, one per channel kind, plus `principal_signed_envelope.json` (the only really-signed fixture). - `interpreters/rust/` — cargo workspace: `aph-core` (types + validation), `aph-conformance` (suite + fixture generator; its `tests/` also carry the multi-party exchange e2e — two-party admit-plus-refusals, three-party relay, cross-notary revocation — the only tests where two parties exchange anything, and the assembled §8.3 recipient algorithm lives in their shared harness), `aph-cli` (binary `aph`: `validate | inspect | golden`), `aph-resolver` (optional §8.4.5 DNS TXT + §8.4.4 `did:web` adapters over `aph-core`'s ports; the ONLY crate that may carry HTTP/DNS/runtime deps — never add an edge to it from the other three), `aph-ts` (wasm binding; outside default-members). - `snapp/` — the exported N Lang bundle (generated by `nlang export`; its integrity digest is content-derived — never hand-edit it). - `skills/`, `commands/`, `.claude-plugin/` — the agent pack this file is the Codex/agents.md entry point for. ## Build and test (exactly what CI runs) From `interpreters/rust/`: ``` cargo test # workspace default members cargo test -p aph-ts # the wasm binding's native tests (NOT in the default set) wasm-pack test --node aph-ts # wasm32 smoke over the signed golden cargo run -q -p aph-cli -- validate <envelope.json> ``` ## Invariants — break these and CI (or a verifier somewhere) breaks you 1. **Strict parsing everywhere.** Every envelope struct is `deny_unknown_fields`; an unknown key is a hard error, never dropped. 2. **The `proof` union is position-sensitive.** It is untagged: a single object (`NotaryAttested`) or a two-proof chain (`PrincipalSigned`, principal first). This is why the JS boundary is JSON text in both directions — never route envelopes through a JS object round-trip. 3. **Absent `attestationMode` means `NotaryAttested`.** A `PrincipalSigned` label is never trusted alone: structure is checked (`APH_E013` on a forged label), and mode downgrades are refused (`APH_E012`). 4. **The error taxonomy is a closed set of fifteen** (`APH_E001`–`APH_E015`). `APH_E014` is TERMINAL ABSENCE of a discovery surface and nothing else — absence advances the §8.4.6 order (`did:key` → DNS TXT → `did:web`). A surface that was offered and then FAILED never falls through: it refuses on the spot under the failure's OWN code (`APH_E008` unreachable, `APH_E003` validity window, `APH_E010` algorithm, …), never a blanket one. `APH_E015` is a delegation mandate REVOKED per the §6.3.3 status list — a withdrawn authorization over still-valid signatures, held distinct from `APH_E003` (authority that ran out on schedule). `APH_E008` now covers any protocol-mandated fetch from a notary-hosted surface, the status list included, not only a notarization request that timed out. 5. **A status verdict must name the key that authenticated it.** `aph_core::credential_status::check_envelope_status` takes the issuer's verifying key BY VALUE, plus a caller-supplied gzip expander (this crate carries no compression dependency — it links into wasm and into a kernel). Neither is an oversight: an unverified status list is an unauthenticated assertion about whether someone's authority still holds, so an unauthenticated verdict is made unrepresentable rather than merely discouraged. Do not add a convenience wrapper that resolves the key internally or defaults it — the whole point is that a caller cannot skip it. Integration recipe in `skills/spec/SKILL.md`. 6. **Signed fixtures are never text-edited.** `examples/principal_signed_envelope.json` carries four real Ed25519 signatures over its bytes. Regenerate it — the recipe is in `skills/spec/SKILL.md`. 7. **Test keys only.** Fixtures sign with RFC 8032 test vectors. Never place production-looking key material — and never any private key — in a committed or published artifact. 8. **Domains are `squillo.com`.** The former domain is retired; do not reintroduce it in examples or docs. 9. **Name-hygiene CI.** Every push runs brand/name hygiene checks over the whole tree; keep tool-vendor names and internal-repo path references out of committed files, and write interpreter paths as `interpreters/rust/<crate>`. ## The live reference deployment `did:web:aph-notary.squillo.com` is a live §8.4.4 publication surface you can resolve against. Its degrade shape and what resolving it exercises are described in `skills/spec/SKILL.md`.