The primitive · open standard

VAID

part of SYNTHERA

A VAID is a portable identity bound to an action an agent takes. It says who acted, with what authority, and who issued that authority – and every action it signs carries a proof-of-possession any party holding the signer's public key can check on sight, with no shared trust domain and no service in between.

What it isThe open standard for verifiable agent identity
LicenseApache-2.0 · spec + reference SDKs
Signing pathRFC 8785 JCS → SHA-256 → Ed25519
InteropRust + Python + TypeScript, byte-for-byte identical
01

The outcome

Before

Every agent action that needs trust across a system boundary takes a bespoke integration or a shared secret. Verifying who did what means being inside the same trust domain, or negotiating a federation agreement first.

After

Any party holding the signer's public key can verify who acted and on what exact request, by checking one signature – with no shared trust domain, no prior relationship, and no service to call.

That single, portable, independently-checkable proof is the primitive the whole category is missing – and the unit SYNTHERA and every tenant is built on.

02

How it works

Think of it like a passport, bound to an action.

A passport says who you are and who vouches for you, and a border officer can check it without calling your home country. A VAID does the same for an agent action: it states the acting identity, the authority it carries, and its lineage – and every action it signs carries a proof-of-possession, so a verifier checks that signature against the acting key rather than taking the sender's word for who called.

This isn't a diagram of verification. It's the real thing, running in your browser: the actual VAID proof-of-possession path against the standard's frozen conformance vector.

Fig · Live verification (runs in your browser)
The signed request

A VAID binds an action to a signature. The reference request below carries a proof-of-possession that verifies against a real, frozen Ed25519 signature – public-key-only, in your browser. Type one character into the body and the proof breaks – that is the point.

The proof-of-possession path
  1. JCS canonicalize
  2. SHA-256 digest
  3. Ed25519 verify
Ready Verifying the reference request…

Real Ed25519 + SHA-256 + RFC 8785 canonicalization over the request payload, checked against the frozen conformance vector – a live demonstration of the proof-of-possession path, not itself a conforming implementation.

THE WHOLE LOOP

Mint on your machine. Send one line. They check it in a browser.

You mint a VAID locally — no account, no call to us. It comes out as a single vaid1: line you can paste into a chat message or an email. Whoever receives it opens the verify page, pastes it in, and gets a verdict. They install nothing. The page is fully client-side: it holds the published kernel key, makes no request when it verifies, and works with the network switched off. That is the point of the credential — checking it requires no cooperation from the party who issued it.

The verdict establishes authenticity and expiry. It does not consult revocation and cannot — there is no published revocation list. The page says so on every result.

THREE LANGUAGES, ONE VECTOR

Interop, made concrete

On the signing and conformance-vector path, the Rust, Python and TypeScript reference SDKs reproduce the same SHA-256 digest and the same Ed25519 signature from the same fixed inputs, with no shared runtime between them. That frozen vector – the one this demo just checked – is the interoperability guarantee, proven rather than claimed.

WHAT STAYS COMMERCIAL

Scope is a statement

The open standard now includes the signing and verification contract and an open reference mint – issuance, delegation and attenuation. What stays commercial is the hosted authority that would run a mint in production: durable revocation, KMS-backed keys and the durable, hash-chained audit-of-record, plus the policy language for what a VAID may do and the mesh and federation control plane. A control plane routes, enforces and mediates between organisations at runtime. Verifying a chain across them needs nothing operated at all. The reference mint proves the shape; it is not that authority. "Hosted authority" names the aggregate of those durable pieces – it is not a separate component that exists today, and the audit and revocation seams are already open in the mint.

03

In detail

Here's precisely what that signature covers – the exact payload, canonicalization and headers the demo just verified, and the guarantees they buy.

01

Cryptographically signed

The action's proof is checked, not asserted. A verifier checks one Ed25519 signature over the canonical request – no trust in the sender required, only the signer's public key.

02

Capability-scoped

The payload binds the exact method, path and body of the action. A signature for one request is useless on any other.

03

Lineage-tracked

At mint time, identity and tenant are bound from the authenticated parent VAID, never from a body-asserted field – so a caller can only ever act as itself.

The signed request

A holder signs a small, exact payload – seven fields, no more, no less – then attaches four headers. The signature covers the canonical bytes of that payload.

canonical path
payload  = { vaidId, method, path,
            bodySha256, tenantId,
            timestamp, clientNonce }
canonical = RFC 8785 JCS(payload)
digest    = SHA-256(canonical)
signature = Ed25519(digest)   # over the 32-byte digest
request headers
x-synthera-vaid:      base64(VAID)
x-synthera-timestamp: 2026-06-04T12:00:00Z
x-synthera-nonce:     0123…cdef
x-synthera-signature: base64(Ed25519 sig)
What's open, and what isn't The standard and its reference SDKs are open source (Apache-2.0): the canonicalization primitive, the request payload, the identity types it binds, and a reference mint that issues, delegates and attenuates VAIDs. There is no server, no database or runtime to stand up. Today the published packages let you sign a VAID-bound request, and verify that request's proof-of-possession against a public key, in Rust, Python and TypeScript; a standalone, public-key-only verifier for the VAID document itself – its issuer signature, authority and lineage, in all three languages – ✓ Shipped. That verifier needs the issuing deployment's kernel public key: it is published, with its thumbprint in three independent channels so you can confirm you have the right one – see the trust anchor. None of the commercial pieces – durable revocation, KMS-backed keys, the durable, hash-chained audit-of-record, the policy engine and the federation layer that make up the SYNTHERA foundation – are needed to check that request proof.
What changed today: a third party can check the whole chain Until today, a delegation chain could only be trusted by trusting the mint that issued it. As of vaid-mint 0.4.0, a third party holding an issuer's kernel public key, plus the ancestor documents a presenter supplies, can walk a full delegation chain and confirm that every child's authority falls inside its parent's: scope, capabilities, and the qualified trust-domain and tenant pair, at every hop – ✓ Shipped. Containment reuses the same matchers the mint applies at issuance, so verify time cannot drift from the check that gated issuance.

It works across organisations – ✓ Shipped. A chain that crosses kernel keys additionally requires a detached consent attestation from the issuer that minted the parent, because nothing inside a VAID document otherwise proves that issuer agreed. A cross-key hop without valid consent never verifies. None of this changed the VAID document: no new field, no signature-version bump, and the frozen mint vector did not move.

What it does not do, stated because these are load-bearing. The presenter must retain and present the ancestors, so a leaf whose ancestor documents were discarded has unverifiable attenuation, and it fails closed as unverifiable rather than passing as satisfied. Full chain verification requires full chain disclosure, which is a real privacy cost and not a footnote. The walk does not consult revocation or expiry; those stay separate questions, and the reference mint's revocation is still in memory only. And consent is time-bounded rather than revocable: an issuer that changes its mind inside an attestation's window has no mechanism to withdraw it, because retraction needs durable revocation, which this implementation does not provide.
Where the reference mint stops: revocation One limitation worth naming before you find it in the source. A signature proves who acted and with what authority they held; whether that authority still stands is the separate question revocation answers, and the reference mint answers it only in memory. The revocation list does not survive a restart, and a restarted mint cannot detect that it restarted, so it again vouches that nothing is revoked and a VAID you revoked beforehand verifies clean. A VAID is a bearer credential, so that is a real exposure and not a cosmetic one. The remediation path is open and already in the package: revocation is a pluggable RevocationCheck seam, three-state and lineage-aware, revoking a parent revokes its children, and it fails closed when a store is unreachable – so injecting a durable backend replaces the in-memory default without touching anything else. Durable revocation is one of the commercial pieces named above. Revocation also sits outside the conformance surface, so no frozen vector polices it.

Get VAID

The reference implementation is open source under Apache-2.0. Source: github.com/solara-associates/vaid

In a coding agent — one command

vaid-skill (0.1.3) is an Agent Skill wrapping the same published SDKs. It installs into whichever of Claude Code, Codex, Cursor, Gemini CLI and GitHub Copilot it finds, and gives the agent four verbs and no more: mint issues a VAID (or, with --parent, an attenuated child whose authority is a strict subset of yours), present packages one into the line you send, verify checks one offline, and revoke marks one revoked on that machine only — which is all anything can do while no revocation list is published.

Any agent
npx vaid-skill

No skill, just the CLI: npx -p vaid-skill vaid --help. More in the quickstart →

Or the SDKs directly

Rust
cargo add vaid-pop
cargo add vaid-client
cargo add vaid-mint
Python
pip install vaid-pop
pip install vaid-mint
pip install vaid-langchain
TypeScript
npm install vaid-pop
npm install vaid-client
npm install vaid-mint

The three implementations version independently, and a fix lands only in the language that had the defect, so installing by name gives you different numbers – vaid-pop is 0.2.1 on crates.io, 0.2.0 on PyPI and 0.3.0 on npm today. Byte-for-byte agreement is asserted at the frozen conformance vector, never at the version number, and each package ships the check so you can confirm that on the artifact you actually received rather than take it from this page. Step 03 of the quickstart →

Mint, sign, verify: the whole loop

Mint a VAID, sign a request with the key it binds, then verify both the VAID and the request against public keys alone. Nothing here calls a service. Copy either column and run it.

Rust · cargo add ring serde_json base64 sha2 hex
use base64::Engine;
use ring::rand::SystemRandom;
use ring::signature::{Ed25519KeyPair, KeyPair};
use sha2::{Digest, Sha256};
use vaid_client::RequestSigner;
use vaid_mint::{verify_vaid_authenticity, AgentClass, ReferenceIssuer, TenantId, VaidIssuer};
use vaid_pop::{request_auth::RequestAuthPayload, vaid_pop::verify_signed_payload};

fn main() {
    let pkcs8 = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()).unwrap();
    let agent_key = Ed25519KeyPair::from_pkcs8(pkcs8.as_ref()).unwrap();  // the agent holds this
    let issuer = ReferenceIssuer::ephemeral(24, "vaid.example").unwrap(); // your own mint
    let vaid = issuer.issue_vaid_with_key(
        AgentClass::new("orchestrator"), "1.0.0".into(), TenantId::new("acme"), None,
        vec!["data.acme".into()], vec!["read".into()],
        agent_key.public_key().as_ref().to_vec()).unwrap();

    let body = br#"{"query": "select 1"}"#;
    let signer = RequestSigner::from_vaid_json(&serde_json::to_vec(&vaid).unwrap(), agent_key).unwrap();
    let headers = signer.sign_headers("POST", "/query", body).unwrap();

    // Verifier side: public keys and the request. No call to the mint.
    let payload = RequestAuthPayload {
        vaid_id: vaid.vaid_id(), method: "POST".into(), path: "/query".into(),
        body_sha256: hex::encode(Sha256::digest(body)), tenant_id: vaid.tenant_id().as_str().into(),
        timestamp: headers.timestamp.parse().unwrap(), client_nonce: headers.nonce.clone(),
    };
    let sig = base64::engine::general_purpose::STANDARD.decode(&headers.signature).unwrap();

    println!("vaid authentic: {}", verify_vaid_authenticity(issuer.kernel_public_key(), &vaid));
    println!("request signed by holder: {}", verify_signed_payload(&payload, vaid.public_key_der(), &sig));
}
Python · pip install vaid-pop vaid-mint
import base64, hashlib
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from vaid_mint import ReferenceIssuer, verify_vaid_authenticity
from vaid_pop import RequestSigner, build_request_auth_payload, verify_signed_payload

agent_key = Ed25519PrivateKey.generate()               # the agent holds this
issuer = ReferenceIssuer.ephemeral(24, "vaid.example") # your own mint
vaid = issuer.issue_vaid_with_key(
    agent_class="orchestrator", version="1.0.0", tenant_id="acme",
    parent_vaid=None, scope_boundary=["data.acme"], capability_set=["read"],
    public_key_der=agent_key.public_key().public_bytes_raw())

body = b'{"query": "select 1"}'
headers = RequestSigner(vaid=vaid, private_key=agent_key).sign_headers("POST", "/query", body)

# Verifier side: public keys and the request. No call to the mint.
payload = build_request_auth_payload(
    vaid_id=vaid["vaid_id"], method="POST", path="/query",
    body_sha256=hashlib.sha256(body).hexdigest(), tenant_id=vaid["tenant_id"],
    timestamp=headers["x-synthera-timestamp"], client_nonce=headers["x-synthera-nonce"])
signature = base64.b64decode(headers["x-synthera-signature"])

print("vaid authentic:", verify_vaid_authenticity(issuer.kernel_public_key(), vaid))
print("request signed by holder:", verify_signed_payload(payload, vaid["public_key_der"], signature))

Both print vaid authentic and request signed by holder as true, in each language's own casing. Run the full quickstart →

SYNTHERA is the trust layer for multi-agent systems: every agent gets a verifiable identity, scoped authority and a tamper-evident record, so software from different teams, vendors and frameworks can act on each other’s behalf without custom glue between every pair.

Join the first cohort shaping the VAID spec.

VAID is an interoperability contract. If you're building on agent identity, help shape the standard as it evolves.

Join the first cohort