- Status: accepted
- Date: 2026-07-27
- Track: T2
- Supersedes: the grant/proof separator rows in
docs/protocol-v1.mdand theEnvelopeFacts-only result wording in ADR 0001
Context
The original unpublished v1 profile prefixed grant and proof signing inputs with
BAP1-GRANT\0 and BAP1-PROOF\0. RFC 7515 defines a JWS signing input as the ASCII bytes
base64url(protected) <> "." <> base64url(payload). RFC 9449 requires a DPoP proof to be a JWT
secured as a JWS. Adding bytes outside that signing input would produce a private signature format,
not a standard compact JWS.
The original authority wording also named only the combined EnvelopeFacts result. A caller needs
to verify a raw grant against an already-trusted issuer before it can safely perform private
runtime work, but a decoded or caller-constructed intermediate must never become an execution
credential.
The package is still unpublished at 0.1.0. This correction is therefore frozen before external
consumption rather than hidden behind a permissive parser.
Decision
- Grant and proof signatures use the exact RFC 7515 JWS signing input:
base64url(protected) <> "." <> base64url(payload). No domain prefix is prepended. BAP1-REQUEST\0remains the request-digest prefix. Its JCS body is[operation, typed(cast_arguments)], where every tagged JSON variant projects to a closed JSON array beginning with its type name and object/array children project recursively. This preserves integer versus integral-float identity across RFC 8785 canonicalization.BAP1-CHAIN\0andBAP1-ARCHIVE\0remain reserved for BAP-04. The grant/proof separator rows are retired.- Deterministic producer functions canonicalize protected headers and payloads, return the exact JWS signing input, and never accept a private key, signer, or signing callback.
- Verification signs and verifies the exact received protected and payload segments. Closed JSON member order is semantically insignificant; a correctly signed received object need not use producer order.
verify_grant/3accepts only raw compact bytes, an exactTrustedIssuer, and an exactExpectedGrant. It returns a closedGrantFactsvalue withauthorization: :not_evaluated, or{:error, :invalid}.check_envelope/2accepts only raw grant/proof credentials and an exactExpectedRequest. It re-verifies the raw grant through the same pure primitive and never acceptsGrantFacts, decoded values, or another caller-constructible intermediate as credentials.GrantFactsandEnvelopeFactsare value-bearing and redacted. Their fields remain directly readable, but fixedInspectoutput and the absence of generic encoders prevent accidental display. They contain only the identifiers, timestamps, and fixed-size fingerprints/hashes named in the public contract; they exclude request argument values, selector values, raw credentials, signatures, JWK containers, and nonces.- Both result types are evidence of cryptographic and contextual verification only. They never select trust, check current revocation, reserve replay, grant execution, or override host policy.
Consequences
- Standard JWS and DPoP implementations can reproduce the signed bytes without a private BAP convention.
- Closed
typand claim schemas, rather than a non-JWS prefix, separate grant and proof objects. - A private runtime can perform standalone grant verification without accepting a forgeable facts value as authority.
- Any implementation that retains the retired grant/proof prefixes, accepts facts as credentials, or describes the result as value-free is nonconforming to v1.
- This ADR does not add trust discovery, signing, live state, replay, revocation, or business authorization to the public package.