Agent DIDs and trust attestations
Every agent in Rivaro has a portable W3C Decentralized Identifier (DID). Use it to:
- Carry agent identity across vendor boundaries (SIEM, ticketing, lineage tools).
- Let any partner system look up an agent's current trust posture without a Rivaro account.
- Verify Rivaro-issued claims about an agent (status, trust score, identity assurance, recent violations) using off-the-shelf JWT-VC libraries.
What you get out of the box
- A
did:keyidentifier per agent, anchored in the organization's existing Ed25519 receipt-signing key. No hosting required, fully self-resolvable. - Optional
did:webidentifiers for customers who want resolvable URIs at their own domain. - A public, signed trust attestation endpoint at
/.well-known/agent-trust/{agentId}that returns a W3C Verifiable Credential as a JWT-VC.
The DID flows alongside the internal agentId in:
TelemetryExportRecord(SIEM batch export).- The Agent-on-a-Page export (PDF + JSON).
- The dashboard agent detail panel (copyable chip + "Check Trust Status" button).
Endpoints
GET /api/agents/{agentId}/export/did?method=key|web&download=true|false # auth required
GET /api/agents/export/did/bulk?method=key # auth required, returns the org-controller DID + every agent DID
GET /api/organizations/me/did.json # auth required, org-level controller DID
GET /.well-known/agent-trust/{agentId} # PUBLIC, returns a JWT-VC (application/vc+jwt)
Hosting did:web
For v1 the demo posture serves everything from the main app host. Production layouts that scale:
- Rivaro-hosted, app-domain. Default in v1. Resolves at
https://{app-host}/.well-known/did.jsonfor the org andhttps://{app-host}/agents/{agentId}/did.jsonper agent. Zero ops effort, but the DID identifier embeds Rivaro's hostname. - Rivaro-hosted, dedicated subdomain (recommended for production). Move the DID surfaces behind a stable subdomain such as
did.rivaro.comso identifiers are decoupled from the app's hostname and can survive product reskins. The endpoint base URL is configurable — setrivaro.did.public-host=did.rivaro.comonce and every emitted DID, service entry, and trust attestationissfollows. - Customer-domain via CNAME. Customers who want their identifiers rooted in their own domain (
did.acme.com) point a CNAME at the dedicated subdomain. The DID document and trust resolver respond at the customer hostname; the signing key is still Rivaro's, so verification continues to work.
Verifying a trust attestation
Any stock JWT-VC verifier can validate the response. Minimal Node example using did-jwt:
import { verifyJWT, ES256KSigner, EdDSASigner } from 'did-jwt';
import { Resolver } from 'did-resolver';
import { getResolver as keyResolver } from 'key-did-resolver';
const jwt = await fetch(`https://app.rivaro.example/.well-known/agent-trust/${agentId}`)
.then((r) => r.text());
const resolver = new Resolver({ ...keyResolver() });
const { payload } = await verifyJWT(jwt, { resolver, audience: undefined });
console.log(payload.vc.credentialSubject); // { trustScore, trustTier, status, identityAssurance, ... }
Manually with the JDK (no third-party dependency):
String jwt = httpClient.send(
HttpRequest.newBuilder(URI.create(trustUrl)).GET().build(),
HttpResponse.BodyHandlers.ofString()).body();
String[] parts = jwt.split("\\.");
byte[] signingInput = (parts[0] + "." + parts[1]).getBytes(StandardCharsets.US_ASCII);
byte[] signature = Base64.getUrlDecoder().decode(parts[2]);
PublicKey orgKey = /* loaded from the org's DID document or /api/organizations/me/did.json */;
Signature verifier = Signature.getInstance("Ed25519");
verifier.initVerify(orgKey);
verifier.update(signingInput);
boolean ok = verifier.verify(signature);
The kid header in the JWT points at the org's verification method
({orgDid}#org-key-1), and the JWT iss claim is the org DID itself. The
agent's DID is in the JWT sub claim and the credential subject's id.
A2A trust check recipe
When agent A receives a request from agent B (identified by a Rivaro DID):
-
Extract
agentDidfrom the inbound request envelope (e.g., from a webhook payload, telemetry record, or DID auth header). -
Resolve the trust attestation:
Send a
GETrequest tohttps://app.rivaro.example/.well-known/agent-trust/{AGENT_ID}with anAccept: application/vc+jwtheader and save the response asattestation.jwt. -
Verify the JWT-VC against the issuer's public key.
-
Read
credentialSubject.trustTier(TRUSTED/STANDARD/HIGH_RISK),credentialSubject.status(ACTIVE/QUARANTINED/ etc.), andcredentialSubject.violationCountto make a routing decision.
The validity window is short (5 minutes by default) so cached decisions don't outlive the underlying governance signals.
Configuration
rivaro:
did:
public-host: app.rivaro.example # used in did:web identifiers and absolute URLs
scheme: https
trust-context-url: https://rivaro.com/contexts/agent-trust/v1
service-type-prefix: https://rivaro.com/spec/v1
Override per environment. The DID identifier and every emitted trust
attestation iss follow these values, so plan the production hostname
before going live — changing it rotates every DID for every agent.