aph-core
When an autonomous agent acts on a human's behalf, this crate is how the receiving side verifies the human actually said yes — a signed, revocable, audience-bound, single-use credential a total stranger can check with public standards alone.
Reference Rust implementation of the APH (Agent per Human) notarization protocol, v0.1.
APH binds an agent's outbound action to a verifiable human authorization: a
W3C Verifiable Credential 2.0-shaped NotarizationEnvelope, whose signing
keys any recipient can resolve from public infrastructure (did:key,
did:web, or a DNS TXT record) with no prior trust relationship.
Trust model note. The specification's 2026-08-13 revision makes the human principal the signer: the envelope carries the principal's own proof, countersigned by the notary, and a
DelegationMandatecarries the human'sprincipalSignature. This crate still implements the pre-revision shape — a single notary proof — so an envelope it verifies proves a notary asserts this human authorized this, not this human authorized this. Do not describe its output as the latter. Support for proof chains,attestationMode, andprincipalSignatureis in progress; see the spec's §7.1.11 andCHANGELOG.md.
This crate is the wire layer and is deliberately squillo-free — it depends
only on serde, serde_json, thiserror, chrono, p256, and base64.
What's here
- Envelope wire types —
NotarizationEnvelopeand its subject objects, with strict (deny_unknown_fields) parsing per spec §7.1. - Mandates —
DelegationMandate(standing authority) andCommunicationMandate(single-use, per-message). - Flow state machines — human-present (7 states) and human-not-present
(5 states); illegal transitions return
APH_E002. - Roles and operations — the §5 permission matrix.
- Error taxonomy — the closed
APH_E001–APH_E015set. - Signing helpers (
crypto) — JCS-style canonicalization, detached JWS, and ES256 sign/verify. - Registered extensions (spec §7.5) —
appleAurAcceptance,linkedMandate.ap2SignedPayloadB64,linkedMandate.vaultMutation.
Usage
let envelope: NotarizationEnvelope = from_str?;
let canonical = canonicalize_rfc8785;
Wire compatibility
Every serde attribute in this crate is load-bearing. Envelopes already
signed by deployed notaries must keep parsing and re-canonicalizing to
identical bytes, because verification recomputes the canonical form and
compares signatures over it. The golden fixtures under tests/golden/ and
the aph-conformance crate are the regression gate.
Two interop notes are deliberate and documented in-source: canonicalization
sorts keys in UTF-8 byte order (not RFC 8785's UTF-16 order) and formats
floats via Rust Display; detached JWS carries DER-encoded ES256
signatures rather than RFC 7518's raw R||S. Both are the deployed wire
behavior and must not be "corrected" without a version bump.
License
Apache-2.0