API Reference

Rust

qhermes-kernel   = "26.0.0"
qhermes-channels = "26.0.0"
qhermes-a2a      = "26.0.0"

All three crates are #![no_std] and #![forbid(unsafe_code)]. qhermes-a2a depends on qhermes-kernel and qhermes-channels. All operations use caller-supplied byte slices and fixed-size stack arrays. No heap allocations.


qhermes-kernel

IdentityIsland::derive(master, deployment, context)

Derives an ML-DSA-65 keypair from a 32-byte master seed via HKDF-SHA3-512. Different deployment or context values yield independent key material. Returns Result<IdentityIsland, KernelError>.

let identity = IdentityIsland::derive(
    &master_seed,
    b"prod",
    b"orchestrator",
)?;

IdentitySigner trait

Implemented by IdentityIsland. Implement this trait to use HSM, TPM, enclave, or any hardware-backed key store.

fn public_key(&self) -> PublicKey<'_>;
fn sign_into(&self, payload: &[u8], out: &mut [u8; SIG_SIZE]) -> Result<(), KernelError>;

perm_tlv(resource, verb, out)

Encodes a (resource, verb) permission into a caller-supplied buffer. Returns bytes written. Resource and verb are opaque byte strings, max 255 bytes each.

not_before(t) / not_after(t)

Construct a 9-byte caveat record from a Timestamp. Concatenate records into a caveats buffer. Timestamps are Unix epoch seconds.

BoundedScope::try_new(raw)

Validates a TLV permission buffer. Returns ScopeEmpty, WireInvalid, or ScopeTooLarge on failure. Maximum 64 permissions.

BoundedCaveats::try_new(raw)

Validates a caveat buffer. Must be a multiple of 9 bytes. Maximum 64 caveats.

issue_credential(issuer, manifest, payload_out, sig_out)

Serializes a DelegationManifest and signs it with the issuer's key. Writes into caller-supplied fixed-size buffers. Returns bytes written to payload_out.

let mut payload = [0u8; MAX_PAYLOAD_SIZE];
let mut sig     = [0u8; SIG_SIZE];
let n = issue_credential(&identity, &manifest, &mut payload, &mut sig)?;

verify_delegation(root_pk, chain, timestamp)

Verifies a slice of Credential records: signatures, depth sequence, scope subset enforcement, caveat validity, and leaf role compliance. Returns credential count on success.

enforce_scope_subset(child, parent)

Checks that every permission in child is covered by parent. A parent permission covers a child when resource and verb match exactly, or when the parent field is b"*". Returns ScopeEscalation on failure.

evaluate_caveats(caveats, now)

Evaluates all time caveats against now. Returns NotBeforeViolation or NotAfterViolation as appropriate.

write_credential_chain(out, chain) / read_credential_chain(wire, chain)

Serialize and deserialize a credential chain. All slices in the deserialized chain point into wire, zero copy.


qhermes-a2a

seal_auth(chain, out)

Serializes a credential chain into a caller-supplied buffer. Returns bytes written. out must be at least AUTH_BLOB_MAX bytes.

let mut buf = [0u8; AUTH_BLOB_MAX];
let n = seal_auth(&chain, &mut buf)?;

verify_auth(root_pk, wire, now, chain_buf)

Deserializes and fully verifies a wire blob. chain_buf is a caller-supplied scratch buffer with at least MAX_DEPTH slots. Returns credential count on success.

sign_agent_card(signer, card_bytes, sig_out)

Signs a canonicalized card payload (JCS/RFC 8785) with ML-DSA-65. The signature field must be absent from card_bytes during signing.

verify_agent_card(root_pk, card_bytes, sig)

Verifies a card signature against root_pk.

kem_offer(peer_ek, context, rng, out)

Initiator side of the ML-KEM-768 handshake. Writes a KEM_OFFER_SIZE-byte ciphertext into out. Returns the SessionKey. context is forwarded to HKDF as domain separation material.

kem_accept(dk, offer, context)

Responder side. Decapsulates the shared secret. Returns the equivalent SessionKey. context must match the initiator's value.


qhermes-channels

derive_kem_keypair(dsa_seed)

Derives an ML-KEM-768 keypair from a 32-byte seed. Passing the same seed used in IdentityIsland::derive binds both identities under the same root secret, with domain separation via b"QHermes-KEM-768-v1".

encapsulate(ek, context, rng)

Encapsulates a fresh shared secret for ek. Returns the ciphertext to send to the receiver and a SessionKey derived via HKDF-SHA3-512.

decapsulate(dk, ct, context)

Decapsulates the shared secret from ct. Returns a SessionKey equivalent to the one returned by encapsulate. context must be identical to the value passed to encapsulate.

SessionKey::seal / SessionKey::open

ChaCha20-Poly1305 encrypt and decrypt over caller-supplied slices. The nonce must not be reused under the same key. SessionKey is zeroed on drop.


Error types

KernelError, A2AError, and ChannelError are all #[non_exhaustive]. Match arms must include a wildcard. Notable variants:

  • ScopeEscalation: child claims a permission absent from the parent scope
  • LeafCannotDelegate: a leaf credential attempted to issue a further delegation
  • ParentKeyMismatch: chain is broken; an issuer key does not match the previous child key
  • NotBeforeViolation / NotAfterViolation: credential evaluated outside its validity window
  • WireVersionMismatch: wire format version not recognized
  • AuthenticationFailed: AEAD tag verification failed

Constants

  • PK_SIZE = 1952: ML-DSA-65 public key, bytes
  • SIG_SIZE = 3309: ML-DSA-65 signature, bytes
  • SEED_SIZE = 32: master seed, bytes
  • MAX_DEPTH = 16: maximum delegation chain depth
  • MAX_SCOPE_PERMS = 64: maximum permissions per credential
  • EK_SIZE = 1184: ML-KEM-768 encapsulation key, bytes
  • CT_SIZE = 1088: ML-KEM-768 ciphertext, bytes
  • KEM_OFFER_SIZE = 1088: KEM offer blob, bytes

Next: Examples