A minimal, self-owned macaroon — first-party caveats only (no third-party discharge). We own this rather than pull a stale hex dep because it is the security-critical token primitive.
A macaroon is an identifier (public, integrity-protected) plus an ordered
list of caveat predicates, authenticated by an HMAC-SHA256 chain:
sig₀ = HMAC(root_key, identifier)
sigᵢ = HMAC(sig_{i-1}, caveatᵢ)The final sig is the credential. Because each caveat extends the chain, a
holder can add caveats to attenuate (restrict) a macaroon offline
(attenuate/2) but cannot remove or alter existing ones without the root key.
Verification recomputes the chain and then requires every caveat to be
satisfied — unknown caveats fail closed, so attenuation only ever narrows
authority.
Wire form: URL-safe base64 of a small JSON envelope {i, c, s} (all fields
themselves base64url), so a token is one opaque bearer string.
Summary
Functions
Append a first-party caveat, restricting the macaroon (holder-side attenuation).
Parse a bearer string back into a macaroon.
Serialize to a single opaque URL-safe bearer string.
Mint a macaroon binding identifier and caveats under root_key.
Verify the HMAC chain under root_key, then require verifier.(caveat) to
return true for every caveat. Returns {:ok, identifier} or {:error, reason}.
Types
Functions
Append a first-party caveat, restricting the macaroon (holder-side attenuation).
Parse a bearer string back into a macaroon.
Serialize to a single opaque URL-safe bearer string.
Mint a macaroon binding identifier and caveats under root_key.
@spec verify(binary(), t(), (binary() -> boolean())) :: {:ok, binary()} | {:error, :bad_signature | :caveat_unsatisfied}
Verify the HMAC chain under root_key, then require verifier.(caveat) to
return true for every caveat. Returns {:ok, identifier} or {:error, reason}.