pub struct PasetoV4SecretMinter { /* private fields */ }Expand description
PASETO v4.public minter — signs claims with an Ed25519 secret key.
Origin-only: holding the secret key is minting power. Pair it with a
PasetoV4PublicVerifier (the matching public key) at the edge. Because
v4.public is signed but not encrypted, only mint non-secret claims this
way — anyone holding the token can read them.
Implementations§
Source§impl PasetoV4SecretMinter
impl PasetoV4SecretMinter
Sourcepub fn from_secret_key(bytes: &[u8; 64]) -> Result<Self, CodecError>
pub fn from_secret_key(bytes: &[u8; 64]) -> Result<Self, CodecError>
Build from a 64-byte Ed25519 secret key in PASETO’s seed || public_key
layout (the V4 secret-key size). Rejects a key whose trailing public half
doesn’t match the seed.
Sourcepub fn generate() -> Result<(Self, PasetoV4PublicVerifier), CodecError>
pub fn generate() -> Result<(Self, PasetoV4PublicVerifier), CodecError>
Generate a fresh Ed25519 keypair, returning the origin minter and its
matching edge PasetoV4PublicVerifier.
Sourcepub fn verifier(&self) -> Result<PasetoV4PublicVerifier, CodecError>
pub fn verifier(&self) -> Result<PasetoV4PublicVerifier, CodecError>
Derive the matching public verifier — e.g. to publish the verify key to an edge while the secret never leaves the origin.
Sourcepub fn secret_key_bytes(&self) -> &[u8] ⓘ
pub fn secret_key_bytes(&self) -> &[u8] ⓘ
Raw bytes of the underlying PASETO V4 secret key — 64 bytes in the
seed || public_key layout.
Origin-only, single-use affordance. The
ServicePrincipalAuthority
returns these bytes to a service-principal install flow exactly once
at provision/rotate time (see
.yah/docs/working/mcp-auth-and-ownership.md §Service principal
bootstrap). Cheers’s own session signing key never reaches this
accessor — it is constructed once at startup and never extracted —
so leaking the cheers secret half is impossible by construction
(the symmetric codecs don’t expose this affordance at all).
Sourcepub fn mint_mcp(
&self,
claims: &McpClaims,
kid: &str,
) -> Result<String, CodecError>
pub fn mint_mcp( &self, claims: &McpClaims, kid: &str, ) -> Result<String, CodecError>
Sign an MCP-call token carrying McpClaims.
Wire convention (R592-B7) — the same envelope kamaji-bin’s
verifier, cheers-mock, and yubaba’s mint_ownership_write_token
already agree on (pinned byte-for-byte in
cheers_test_support::fixtures): claims serializes directly as the
token’s FLAT top-level JSON payload — no wrapping claim key, exp/
iat stay i64 Unix seconds — signed via pasetors’ LOW-LEVEL
PublicToken::sign, never the high-level Claims wrapper (which
hard-rejects non-string registered claims). kid rides in the PASETO
footer as {"kid":"<kid>"}; the matching
PasetoV4PublicVerifier::verify_mcp_at requires it.
The caller owns iat/exp (mirrors the session-claim path) — TTL
policy for MCP tokens belongs upstream of this primitive. kid
identifies which published key this mint used — same string a
verifier’s JWKS/footer lookup keys off of.
Serializes via serde_json::Value rather than serializing claims
directly: every reference wire producer (yubaba’s
mint_ownership_write_token, cheers-mock’s MockIssuer::mint,
cheers_test_support::fixtures::sign_fixture) builds its payload as a
Value/json!{} map, whose default (non-preserve_order)
BTreeMap-backed key order is alphabetical — NOT the struct’s
field-declaration order #[derive(Serialize)] would emit. Routing
through Value here reproduces that same alphabetical byte layout,
which is what makes mint_mcp’s output byte-identical to the golden
fixtures (verified in cheers-server/tests/golden_fixtures.rs).
Deserialization is unaffected either way (JSON objects key-match by
name), but the mint side has to match on the wire.