Skip to main content

vaid-mint (Python)

The Python mirror of the Rust vaid-mint crate: the open, self-hostable reference mint for the VAID (Verifiable Agent Identity) standard.

  • mint_root — mint a root/operator VAID (BYO-key with proof-of-possession, or generate-and-discard), gated by an explicit AuthorizationGate.
  • mint_childattenuated delegation: an authenticated parent mints a child whose authority is always a subset of its own (child ⊆ parent).

Trust model — read this before using the mint

Upgrading from 0.1.1? Expiry enforcement is a ⚠️ breaking behavioral change despite the patch version bump: verify_vaid now returns False for expired VAIDs that previously passed. Read CHANGELOG.md before upgrading. Note that 0.1.2 is not yet on PyPIpip install vaid-mint still serves 0.1.1, which has neither the seam nor expiry enforcement described below.

Concern Reference mint (this package) Hosted / commercial
Revocation Pluggable (RevocationCheck); default is in-memory, non-durable Durable, hash-chained
Expiry (TTL) Enforced at verification (hard reject) Enforced
Auth Pluggable (AuthorizationGate) Pluggable
Audit Pluggable (AuditSink) Pluggable

Revocation now has a pluggable seam, but the shipped default is still non-durable. As of 0.1.2 there is a RevocationCheck protocol: a self-hoster can inject their own durable, restart-surviving backend via ReferenceIssuer.with_revocation_check without patching the package. What ships by default, however, is still the concrete in-memory revoked set — it does not survive a restart. If the mint process restarts and you have not wired a durable RevocationCheck, previously revoked VAIDs are revocable again. The seam closes the "no extension point" gap; it does not by itself make revocation durable. That is your responsibility to wire, or the hosted authority's to provide.

from vaid_mint import InMemoryRevocationList, ReferenceIssuer

class MyDurableRevocations:
    """Your own restart-surviving store (or a refreshed snapshot of one)."""
    def is_revoked(self, vaid_id: str) -> bool:
        return vaid_id in load_deny_list()

# The injected check is consulted IN ADDITION TO the built-in in-memory set —
# a VAID is rejected if EITHER reports it revoked.
issuer = ReferenceIssuer.ephemeral(1).with_revocation_check(MyDurableRevocations())

# Or wire the seam with the shipped in-memory list before a durable backend exists:
revocations = InMemoryRevocationList()
issuer = ReferenceIssuer.ephemeral(1).with_revocation_check(revocations)
revocations.revoke(vaid["vaid_id"])
assert not issuer.verify_vaid(vaid)

If you're running this in production, mitigate as follows:

  • Mint short-lived VAIDs. vaid_ttl_hours controls issuance TTL, and DEFAULT_VAID_TTL_HOURS (1h) is the recommended baseline. Expiry is now enforced at verification — an expired VAID hard-fails verify_vaid, not merely reported — so a short TTL is a real backstop that shrinks the exposure window for a leaked or compromised VAID even without durable revocation. Treat TTL as your primary control today.
  • Inject a durable RevocationCheck (e.g. backed by a shared store or a periodically-refreshed snapshot of one) if you need revocation to survive restarts. The injected check is consulted in addition to the built-in in-memory set, so enabling it never disables existing behavior.
  • Or front the mint with a revocation-aware proxy or allowlist — e.g. a sidecar or gateway that checks a durable deny-list before forwarding to verify_vaid.
  • Do not rely on the default configuration alone for revocation guarantees that must survive a process restart.

The RevocationCheck seam mirrors the injection style of AuthorizationGate and AuditSink, with one deliberate difference: its default is not an honest no-op. For revocation, a no-op default would mean nothing is ever checked — a silent functional regression, not a neutral "not wired yet" state — so the reference keeps its working in-memory set as the default. A NeverRevoked no-op is available as an explicit opt-in. The hosted product additionally offers a durable, hash-chained revocation store; the open package now gives you the seam to plug your own into.

Unguarded defaults: authorization and delegation

This is a reference implementation with two deliberate, unguarded defaults:

  1. mint_root has no authorization gate by default (PermitAll). Anyone who can call this code can mint a root VAID. Supply a real AuthorizationGate for anything beyond local experimentation.
  2. mint_child is intentionally ungated — attenuation is the authorization. Any holder of a valid parent VAID can mint children from it; a child can only narrow scope/capabilities relative to its parent, never widen (child ⊆ parent). Possession of a parent VAID is itself the authorization boundary for delegation here. Treat parent-VAID custody with the same care as a credential.

Neither of these is a security recommendation for production use — they are the honest defaults of a self-hostable reference mint. See the sections below for where each is enforced in code.

from vaid_mint import ReferenceIssuer, InMemoryAudit, MintService, VaidSeed

issuer = ReferenceIssuer.ephemeral(24)
mint = MintService(issuer, InMemoryAudit())
root = mint.mint_root(VaidSeed(
    agent_class="orchestrator", version="1.0.0", tenant_id="acme",
    scope_boundary=["data.acme"], capability_set=["read", "write"],
))
assert issuer.verify_vaid(root)

The split

This is the open engine of a HashiCorp-Vault-style split. KMS-backed kernel keys and the audit-of-record are the closed managed authority and are not here. Revocation is the seam worth naming plainly rather than filing under "commercial": as of 0.1.2 this package ships a pluggable RevocationCheck seam — additive, with a non-durable in-memory default — and VAID expiry (TTL) is hard-enforced at verification. What stays commercial is durable revocation itself: a restart-surviving, hash-chained store. The package ships the seam, not the durability.

Concern Here (open) Hosted / commercial
Kernel signing key ephemeral or caller/seed-supplied bytes KMS-backed, rotated
Revocation pluggable (RevocationCheck), in-memory default — see Trust model durable, hash-chained
Expiry (TTL) enforced at verification (hard reject) enforced
Audit in-memory / no-op sink audit-of-record
Policy / mesh / federation control plane

mint_root is gated by an AuthorizationGate that defaults to PermitAll — a reference-implementation choice, not a security recommendation; production deployments should pass a real gate to MintService.

mint_child is intentionally ungated because attenuation is the authorization: any holder of a valid parent VAID can mint children from it, and a child can only narrow scope/capabilities relative to that parent, never widen (child ⊆ parent). So possession of a parent VAID is itself the authorization boundary for delegation — treat parent-VAID custody with the same care as a credential.

Cross-language byte-identity

Proof-of-possession reuses the vaid-pop primitive verbatim. The signed VAID document is proven byte-identical to the Rust mint by the vendored frozen vector vaid_mint/vectors/mint_v1.json (the same mint_v1.json the Rust mint_conformance test asserts). Run the packaged firewall:

vaid-mint-conformance          # exit 0 = PASS (installed mint == frozen vector)

Per Decision B this is self-consistent within this repo (Rust == Python); it is not byte-conformant against the managed authority's (still-moving) VAID format.

Install (local dev)

vaid-mint depends on vaid-pop. For a local checkout, install both editable:

pip install -e python/vaid-pop
pip install -e python/vaid-mint --no-deps

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vaid_mint-0.1.2.tar.gz (21.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vaid_mint-0.1.2-py3-none-any.whl (24.0 kB view details)

Uploaded Python 3

File details

Details for the file vaid_mint-0.1.2.tar.gz.

File metadata

  • Download URL: vaid_mint-0.1.2.tar.gz
  • Upload date:
  • Size: 21.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for vaid_mint-0.1.2.tar.gz
Algorithm Hash digest
SHA256 84446d89a77fa868ffbde937ba62b37a27013653c481e3b4a708e5e92527973e
MD5 cd15a38e8366d5456d2449eaab91160e
BLAKE2b-256 f1f9b2dcc3d6266da04fa8e7e7233b8c56cfbcfa1242a0b087b90459fe884096

See more details on using hashes here.

File details

Details for the file vaid_mint-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: vaid_mint-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 24.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for vaid_mint-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9fe3f0268e5c1799ab036b5739f05ea26229a91d200f2dfeed37ea3c57a25c22
MD5 7733f1dfb5122305a7d76c0e122c46bb
BLAKE2b-256 e28ba40c3ef90feac3790e5869234d8f6bca2035316d3ae7b46935feea1242db

See more details on using hashes here.

Release history Release notifications | RSS feed

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.3

2 files

This release

0.1.2 This release

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page