SigilGuard. Envelope
(SigilGuard v0.2.0)
View Source
SIGIL envelope implementation for MCP JSON-RPC _sigil metadata.
Implements the envelope format defined by the SIGIL protocol:
- Canonical bytes: lexicographic key order, compact JSON, no whitespace,
excluding
signatureandreason - Ed25519 signature, base64url-encoded (no padding)
- ISO 8601 timestamp with millisecond precision (UTC)
- 16-byte cryptographically random nonce, hex-encoded
Signing
Signing requires a module implementing SigilGuard.Signer:
envelope = SigilGuard.Envelope.sign("did:sigil:abc", :allowed, signer: MySigner)Verification
:ok = SigilGuard.Envelope.verify(envelope, public_key_b64u)
Summary
Functions
Produce the canonical byte representation for signing.
Generate a 16-byte cryptographically random nonce as hex.
Generate an ISO 8601 timestamp with millisecond precision.
Sign an envelope with the given identity and verdict.
Verify an envelope's signature against a base64url-encoded Ed25519 public key.
Types
Functions
Produce the canonical byte representation for signing.
Fields are serialized as compact JSON with lexicographic key order,
no whitespace, excluding signature and reason. This matches the
Rust sigil-protocol crate's canonical_bytes implementation.
@spec generate_nonce() :: String.t()
Generate a 16-byte cryptographically random nonce as hex.
@spec generate_timestamp() :: String.t()
Generate an ISO 8601 timestamp with millisecond precision.
Sign an envelope with the given identity and verdict.
Options
:signer— module implementingSigilGuard.Signer(required):reason— optional human-readable reason string:timestamp— override timestamp (for testing):nonce— override nonce hex (for testing)
Returns a map suitable for embedding as the _sigil field in MCP JSON-RPC params.
Verify an envelope's signature against a base64url-encoded Ed25519 public key.
Returns :ok if the signature is valid, or {:error, reason} otherwise.
Never raises on malformed input — envelopes arrive over the wire and
must be treated as adversarial.
Error reasons
:invalid_envelope— envelope is not a map, or the key is not a string:missing_field— a required field (identity,verdict,timestamp,nonce,signature) is absent or not a string:invalid_verdict— verdict is not"Allowed","Blocked", or"Scanned":invalid_base64— the public key or signature is not valid base64url:invalid_key— the public key does not decode to 32 bytes:invalid_signature— the signature has the wrong size or does not verify
Verification is stateless, matching the sigil-protocol crate: replay
protection (tracking seen nonces, enforcing timestamp freshness) is the
caller's responsibility.