VAID · quickstart

Verify a VAID without talking to us.

Three steps. About twenty lines. No account.

By the end of this page you will have minted a VAID, signed a request with the key it binds, and verified both against public keys alone. Nothing here calls a service we run, nothing asks who you are, and nothing needs a credential from us. That is not a convenience of the quickstart. It is the property the whole primitive exists to provide, so the honest way to show it is to let you run it.

01Install

Pick a language — or skip that.

The reference implementation is open source under Apache-2.0. There is no server, no database and no runtime to stand up, because verification is a computation and not a request.

If you work in a coding agent, one command

vaid-skill is an Agent Skill: it teaches your agent when to mint a VAID, how to present one, and how to check one it was handed. It is a thin wrapper over the same published SDKs below and implements no cryptography of its own.

Any agent
npx vaid-skill

It detects which agents are present and installs into each: Claude Code, Codex, Cursor, Gemini CLI and GitHub Copilot. Add --global for your home directory instead of this project, or --dry-run to see the plan and change nothing. Re-running it is safe: the agents that share one context file get a delimited block rewritten in place, never a second, drifting copy.

The skill is also published from this domain, for the skills CLI:

From solara.associates
npx skills add https://solara.associates

Installs of skills published from solara.associates, counted by skills.sh

This reads /.well-known/agent-skills/index.json, which publishes a SHA-256 for the skill file beside it. The CLI recomputes that hash over the bytes it downloaded and installs nothing if the two disagree — so a substituted file fails rather than installs. It fetches the skill only; the vaid commands below still come from the published package, which is why the skill invokes them as npx -p vaid-skill. Use npx vaid-skill above if you want both in one step.

Four verbs, and there is no fifth

vaid
vaid mint      issue a VAID — or, with --parent, an attenuated child
               whose authority is a strict subset of yours
vaid present   package one into the single line you send to someone
vaid verify    check one you received, offline, against a pinned key
vaid revoke    mark one revoked ON THIS MACHINE ONLY — read below
What verify does and does not tell you It establishes authenticity, expiry, delegation containment, and whether the issuer configured a real identity. It does not consult revocation, and it cannot: there is no published revocation list, and an offline verifier could not reach one if there were. Read a pass as genuinely issued and in date, never as currently authorised. That is also why vaid revoke only writes a local file — it says so on every run.

vaid-skill is 0.1.3 today. Prefer the raw CLI? npx -p vaid-skill vaid --help works without installing the skill into anything — npx resolves its argument as a package, so -p is what reaches a bin whose name is not the package's.

Or use the SDKs directly

Rust
cargo add vaid-pop vaid-client vaid-mint
cargo add ring serde_json base64 sha2 hex
Python
pip install vaid-pop vaid-mint
TypeScript
npm install vaid-pop vaid-client vaid-mint
On the version numbers 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. Today vaid-pop is 0.2.1 on crates.io, 0.2.0 on PyPI and 0.3.0 on npm. Byte-for-byte agreement between them is asserted at the frozen conformance vector, never at the version number, and step 03 is how you confirm that yourself rather than take it from this page.
02Mint, sign, verify

The whole loop, in one file.

Mint a VAID, sign a request with the key it binds, then verify both the VAID and the request against public keys alone. The verifier half of each snippet holds a public key and the request, and nothing else. It never calls the mint that issued the identity, because it does not need to.

Rust · src/main.rs
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 · quickstart.py
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))
what each prints
rust  $ cargo run
        vaid authentic: true
        request signed by holder: true

py    $ python quickstart.py
        vaid authentic: True
        request signed by holder: True
What you just proved, and what you did not You proved authenticity: the VAID was genuinely issued under that kernel key, and that exact request was signed by the key the VAID binds. Change one byte of the body, the method or the path and the second check turns false. What you have not proved is standing, meaning whether that authority is still current, which is the separate question revocation answers. The reference mint answers it in memory only and loses it on restart, so read where the reference mint stops before you rely on it.
03Check the artifact you received

Do not take the interop claim from us.

Every package in all three languages ships a conformance firewall as an executable. It reproduces the frozen vectors from the package you actually installed, not from a copy in our repository and not from this page. Run it wherever you like and compare the hex by eye: same digest, same signature, different runtimes, different version numbers.

Rust
cargo install vaid-pop
vaid-pop-conformance
Python
vaid-pop-conformance
vaid-mint-conformance
TypeScript
npx -p vaid-pop vaid-pop-conformance
npx -p vaid-mint vaid-mint-conformance
npx -p vaid-client vaid-client-conformance
the digest every implementation must reproduce
CROSS-LANGUAGE PoP FIREWALL: PASS — installed signer == frozen vectors, byte-for-byte
  request digest    = ee474ba87d703ebeacf663d7d6a2f15319bdef285c5b702e336d0f4af5b61327
  request signature = 77e79744c362d352ce678992a3e3934fa57c33c3f307f6ffbe6ffc4ec5e726e0
                      96844fd8552db819b21a3e49e3b7796e23d1e10d4a03699285df4871f62e1502

If your own implementation reproduces those bytes, it conforms. If it does not, the diff shows where it diverged. That is the entire interoperability contract, and it is checkable by a stranger who has never spoken to us, which is the only kind of claim worth making about identity.

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.

It ran. Now tell us what broke.

VAID is an interoperability contract, and contracts get better under adversarial reading. If you ran this and something was wrong, unclear, or missing, that is the most useful thing you can send us.

Talk to us