Request model
A VTA defines its operations, such as signing a payload, listing keys, or releasing a secret, once, as a typed Trust Task, and the same dispatch serves that definition over REST, DIDComm, and the Trust Spanning Protocol (TSP). That shape explains why switching between transports, as connect_auto does automatically, changes almost nothing about what your application can do. The exception is sealing a secret, which needs DIDComm.
The interfaces table on the overview page also lists the offline CLI, a separate local administration tool for a self-hosted VTA’s on-disk store.
One operation, three transports
A Trust Task is a typed operation with a URI (for example, the sign operation), a request shape, and a response shape, dispatched from a single table inside the VTA:
- REST wraps that dispatch in an HTTP request.
- DIDComm wraps the same dispatch in an end-to-end-encrypted message.
- TSP wraps the same dispatch in a TSP message, on a self-hosted VTA that enables it.
The operation’s authorisation checks, audit logging, and business logic are shared across those three transports. The VTA runs the same underlying function whichever wrapper the request arrived in, so the transports stay in step with each other.
This is why your application code does not need transport-specific logic. VtaClient::connect_auto picks REST or DIDComm based on whether a mediator is configured, and calls such as sign, list_keys, and vault_get work the same way on either transport. Storing a vault secret and opening a released one are the exception: both seal the secret with DIDComm authcrypt, so an application that does either connects through a mediator.
Authenticating with your DID
A VTA uses a DID, not an API key or bearer token, as its root of trust. Every caller authenticates as a DID. Over DIDComm and TSP, the message envelope itself proves the sender’s DID, with no challenge or bearer token. Over REST, the caller uses the challenge-response exchange the VTI specification defines:
- You request a challenge (
POST /auth/challenge) naming the DID you’re authenticating as. - The VTA returns a single-use nonce bound to a session.
- You sign that nonce with the private key behind your DID and submit it (
POST /auth/). - The VTA verifies the signature against your DID’s public key and, if your role and access control list (ACL) entry check out, issues a short-lived access token and a longer-lived refresh token.
No reusable long-lived secret is transmitted during the challenge itself: the nonce is single-use, and the signature proves possession of your private key without ever transmitting it. The refresh token issued alongside the access token is a bearer credential, but it rotates on every use. Replaying a token that was already rotated ends the whole session, apart from a retry within a short grace period, 30 seconds by default. Compare this to an API key, which is itself the credential, valid for as long as it isn’t rotated, and just as usable by whoever intercepts it as by you.
Session lifetime
The access token this exchange issues is short-lived by design. By default, it lasts:
- 15 minutes under normal authentication.
- 5 minutes if your session required step-up (re-authentication for a sensitive operation).
By default, the refresh token lasts 24 hours. It exchanges for a fresh access token without repeating the signature challenge, so within its lifetime a long-running service stays authenticated without signing a new challenge. Each exchange issues a new refresh token and invalidates the one it replaces.
The VTA’s configuration can change both lifetimes, through VTA_AUTH_ACCESS_EXPIRY and VTA_AUTH_REFRESH_EXPIRY. A stepped-up access token lasts one third of the access-token lifetime, with a 60-second minimum.
Which client to use for your language
The VTA’s client library is vta-sdk, a Rust crate published on crates.io, with API documentation on docs.rs. pnm is built on it, and the code examples in the integration guides use it. VtaClient wraps the challenge-response exchange, token refresh, and the choice between REST and DIDComm, so a Rust application calls operations such as sign or vault_get directly.
vta-sdk is Rust-only. From other languages, two common routes are:
- HTTPS API: call the VTA’s REST endpoints directly, authenticating your DID through the challenge-response exchange described in Authenticating with your DID.
- MCP bridge: when your agent already runs inside an MCP host,
vta-mcpexposes the VTA’s operations as tools, with no client code to write. See Connect an MCP host to your VTA.
Related
Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.