Detached JWS signature envelope: RFC 7515 compact serialization with the
RFC 7797 b64=false unencoded, detached payload, Ed25519 signatures
verified through :crypto. Verify-only — the package never signs,
never accepts a private key on any function, and performs no key
discovery or trust selection; hosts supply the trusted keys.
What is signed is the RFC 7797 §3 input:
ASCII(BASE64URL(JCS(protected_header))) || "." || JCS(signed_attributes)so a standard, b64=false-aware JOSE tooling stack can verify the same
bytes (the ratified preimage form,
byte-disjoint from the design's earlier sketch). Producer contract: the
protected header segment must be the JCS serialization of the exact
four-member header; a producer using any other member order produces a
legitimately-signed JWS this verifier denies :signature_not_verified.
This package is deliberately narrower than generic JOSE for determinism.
The envelope shape (closed world, value-free denials):
{"protected": {"alg":"EdDSA","b64":false,"crit":["b64"],"kid":"…"},
"signed_attributes":{"algorithm":"Ed25519","content_digest":"sha-256:…",
"created_at":"2026-08-20T00:00:00Z","key_id":"…",
"purpose":"blueprint"},
"signature": "<86-char unpadded base64url>"}kid must equal the signed key_id (key substitution prevention);
key_id is dot-free so the unencoded payload can never contain .
(RFC 7797 §5.2 forbids it in compact serializations); created_at is
Z-form whole seconds. Attestations use the identical envelope with
purpose/content_digest replaced by a registered kind and a
statement_digest; the kind registry is empty until kinds become data,
so every attestation denies :attestation_malformed today (fail-closed).
Because content_digest and purpose are inside the signed bytes, a
signature cannot be lifted onto a different artifact, digest, or
purpose. Errors are value-free.
Signature verification produces evidence only; it never authorizes an operation.
Summary
Functions
The signed attributes of a signature entry as an Attributes value.
Shape-validated only — no signature has been checked; only verify/2
establishes the signature fact. Do not consume attributes from an
envelope that has not verified.
The RFC 7797 b64=false signing input of a signature entry (the tagged
algebra as decoded by Json): BASE64URL(JCS(protected)) || "." || JCS(signed_attributes). Envelope violations deny with their
verify/2 reason.
The RFC 7515 detached compact serialization
(BASE64URL(JCS(protected)) .. BASE64URL(signature), empty payload
segment per Appendix F) for standard-tooling cross-checks.
Verify a signature entry against host-supplied trusted keys. Returns
facts, never authorization: {:ok, :verified} when any key supplied for
the entry's key_id verifies the envelope; {:error, :signature_key_unsupported} when no supplied key carries that id;
{:error, :signature_algorithm_unsupported} when the header names a
non-EdDSA algorithm or no matched key is usable Ed25519;
{:error, :signature_malformed} for envelope violations;
{:error, :signature_not_verified} when the bytes do not verify.
Verify an attestation entry (identical envelope; kind and
statement_digest replace purpose and content_digest). The kind
registry is empty, so a well-formed attestation denies
{:error, :attestation_malformed} today — the fail-closed posture, not
a defect. Shape errors use :attestation_malformed; algorithm denials
(alg/algorithm not EdDSA/Ed25519) share the signature vocabulary by
design — the envelope's algorithm posture is one concern. When the first
kind registers, the single-clause match below fails loudly on the
now-possible {:ok, _} and the identical check/3 wiring used by
verify/2 is added deliberately; the envelope machinery (parts/2) is
already shared.
Types
Functions
@spec attributes(AgentBlueprintProtocol.Json.value()) :: {:ok, AgentBlueprintProtocol.Signature.Attributes.t()} | {:error, reason()}
The signed attributes of a signature entry as an Attributes value.
Shape-validated only — no signature has been checked; only verify/2
establishes the signature fact. Do not consume attributes from an
envelope that has not verified.
@spec signing_input(AgentBlueprintProtocol.Json.value()) :: {:ok, binary()} | {:error, reason()}
The RFC 7797 b64=false signing input of a signature entry (the tagged
algebra as decoded by Json): BASE64URL(JCS(protected)) || "." || JCS(signed_attributes). Envelope violations deny with their
verify/2 reason.
@spec to_compact(AgentBlueprintProtocol.Json.value()) :: {:ok, binary()} | {:error, reason()}
The RFC 7515 detached compact serialization
(BASE64URL(JCS(protected)) .. BASE64URL(signature), empty payload
segment per Appendix F) for standard-tooling cross-checks.
@spec verify(AgentBlueprintProtocol.Json.value(), [ AgentBlueprintProtocol.Signature.PublicKey.t() ]) :: {:ok, :verified} | {:error, reason()}
Verify a signature entry against host-supplied trusted keys. Returns
facts, never authorization: {:ok, :verified} when any key supplied for
the entry's key_id verifies the envelope; {:error, :signature_key_unsupported} when no supplied key carries that id;
{:error, :signature_algorithm_unsupported} when the header names a
non-EdDSA algorithm or no matched key is usable Ed25519;
{:error, :signature_malformed} for envelope violations;
{:error, :signature_not_verified} when the bytes do not verify.
@spec verify_attestation(AgentBlueprintProtocol.Json.value(), [ AgentBlueprintProtocol.Signature.PublicKey.t() ]) :: {:ok, :verified} | {:error, reason()}
Verify an attestation entry (identical envelope; kind and
statement_digest replace purpose and content_digest). The kind
registry is empty, so a well-formed attestation denies
{:error, :attestation_malformed} today — the fail-closed posture, not
a defect. Shape errors use :attestation_malformed; algorithm denials
(alg/algorithm not EdDSA/Ed25519) share the signature vocabulary by
design — the envelope's algorithm posture is one concern. When the first
kind registers, the single-clause match below fails loudly on the
now-possible {:ok, _} and the identical check/3 wiring used by
verify/2 is added deliberately; the envelope machinery (parts/2) is
already shared.