Advanced clients and OAuth
Start with the shared-account tutorial for a complete app. The generated reference covers public signatures, option fields, return types, and source comments across all eight library entry points. This page maps imports to tasks; chain, signing, and errors provide operation details. Code fragments below assume their named inputs already exist.
Choose a client
Section titled “Choose a client”Start new applications with TasraClient from tasra-sdk/app. The runtime class
is separate from the root module’s existing TasraClient session-client type.
Install the latest SDK with npm install tasra-sdk@latest; see
installation for runtime and TypeScript setup.
| Entry point | Import | Use it for | Requires |
|---|---|---|---|
new TasraClient({manifest}) / TasraClient.fromManifest(url) |
tasra-sdk/app |
Create slots, use wallets and credentials, encrypt and sign | Public deployment manifest; wallet and durable store for slot creation |
createFileStore |
tasra-sdk/app/node |
Private durable state for a Node application | A private local directory |
createTasraChainClient |
tasra-sdk/chain |
Read contracts and discover the network | RPC, chain ID, deployment addresses |
createTasraWriteClient |
tasra-sdk/chain |
Create slots, fund them, change on-chain state | Deployment configuration and funded transaction signer |
createCommitteeSlotClient |
tasra-sdk/chain |
Credential-gated threshold encryption/decryption and FROST signing | Chain client, slot, holder, credentials and verifier proofs |
createTasraSlotClient |
tasra-sdk/chain |
Chain-discovered managed sessions | Chain client, identity and session authentication |
createTasraClient |
tasra-sdk |
Managed sessions with explicit endpoints | Keeper URLs and session authentication; verifier for credential redemption |
The remaining factories support advanced integrations and existing applications.
Managed-session local decryption requires an exportable BLS slot. Threshold
custody uses the committee client; it does not export the master key.
For Ethereum signatures, see signEoaDigest, not committee FROST signing.
Common results
Section titled “Common results”| Call | Returns | Related guide |
|---|---|---|
tasra.check() |
Chain and registry availability; authorization compatibility remains unknown | Quickstart |
tasra.slots.create(request) |
Ready typed slot with its committed access policy provisioned | Application API |
tasra.wallets.create() / tasra.wallets.connect(provider) |
Local wallet / promise of an external-wallet adapter | Application API |
tasra.wallets.fromSlot(slotId, {authorize}) |
Promise of a threshold Ethereum wallet with named durable transfers | Shared account |
tasra.identities.create() |
Fresh holder identity with a DID and explicit private-key export | Credentials |
tasra.credentials.issue(input) / authorize(input) |
SD-JWT credential / operation authorizer | Credentials |
parsePinnedNetworkManifest(text, sha256) |
Validated NetworkManifest; throws on checksum or schema mismatch |
Fuji configuration |
createTasraChainClient(config) |
TasraChainClient with client and typed readers |
Chain reference |
chain.readers.keyRegistry.getKeySlot(slotId) |
Promise of slot state, including exists, mode, publicKey, epoch, cancelled |
Address lookup |
addressFromEoaPubkey(pubkey) |
EIP-55 checksummed Ethereum address | Address lookup |
committeeSignEoaDigest(options) |
Promise of {groupPublicKey, r, s, yParity} using request-bound authorization |
Complete tutorial |
signEoaDigest(options) |
Promise of {groupPublicKey, r, s, yParity} |
Signing |
client.openSession(slotId, auth) |
Promise of a managed Session |
Exportable vault |
Application entry exports
Section titled “Application entry exports”tasra-sdk/app provides TasraClient, the manifest loader, wallet and identity
adapters, credential helpers, slot creation and native approval helpers.
tasra-sdk/app/node provides createFileStore; keep this Node-only import out of
browser bundles. See the application guide for the normal
workflow and generated application reference for complete signatures.
Main entry exports
Section titled “Main entry exports”| Group | Symbols |
|---|---|
| Managed client | createTasraClient, TasraClient, Session, SessionAuth, VpJwtAuth, OpenSessionOpts, SignOpts |
| Errors | TasraError, TasraHttpError, AuthDeniedError, NodeUnreachableError, ThresholdNotMetError, SlotRotatedError, CommitteeAuthorizeError, DcqlMalformedError, VerifierAgentSessionError, isAuthDenied, isRetryable |
| Crypto | encryptEnvelope, decryptWithMasterKey, toBytes, fromBytes, buildTasraText, parseTasraPost, isTasraPost, GroupEnvelope |
| Identity-scoped IBE | ibeEncrypt, ibeCombineExtract, ibeCombineDecrypt, ibeDecryptWithKey, requestIbeExtractionPartials; large objects: ibeSealBlob, ibeOpenBlob, ibeUnwrapBlobKey, ibeBlobDecryptKey, ibeDecryptBlobChunk, ibeBlobChunkRange, ibeBlobDigest, IbeBlobHeader |
| Node client | fetchMpk, fetchAndAssembleKey |
| Signing | signCustody, signWithShardDelivery, signUserRequest, signEoaDigest, ethSignatureV, addressFromEoaPubkey, aggregateFrostSignature, verifyFrostSignature |
| Decryption (threshold) | decryptCustody, decryptWithShardDelivery, combineDecryptShares, verifyDecryptShare |
| Verifier auth + JWT | redeemCredential, redeemRenewalToken, createRenewal, revokeRenewal, issueAdminCredential, revokeSlotUser, verifyPresentation, verifyVpJwt, decodeJwtClaims, jwtExpMs, isJwtExpiringSoon + IssuedToken, JwtClaims |
| DCQL (OID4VP-DCQL) | validateDcql, evaluateDcql, selectDcql, canonicalizeDcql, isOid4vpRule, jsonCredential, evaluateIdentityScoped, scopeCovers, DcqlMalformedError, DCQL_MAX_RULE_LEN + DcqlQuery, CredentialView |
| Verifier Agent sessions | createOid4vpSession, pollOid4vpSession, waitForSession, payloadDigest, VerifierAgentSessionError; oauth kind: createOauthSession, submitOauthResponse + CreateSessionParams, CreateOauthSessionResult, SessionStatusResult |
| DPoP (RFC 9449) | createDpopKey, auth0DpopSigner, jwkThumbprint, accessTokenHash, isHeaderSafeNonce + DpopSigner, DpopKey |
| Committee tokens | not on the main entry — see tasra-sdk/committee |
| Slot funding | httpFaucet (sovereign slot creation lives in tasra-sdk/chain) |
| Utils | hexToBytes |
For new apps, use tasra.slots.bls(...) and tasra.slots.frost(...) for
credential-gated threshold custody. createCommitteeSlotClient remains available
for advanced committee integrations. Managed
Session.decrypt requires an exportable personal-vault slot; the session primitives,
verifier-auth helpers, and crypto are what it composes (use them directly when you
need finer control). To resolve endpoints from chain instead of hardcoding them, use the
tasra-sdk/chain clients (createTasraSlotClient / createCommitteeSlotClient).
Verifier-auth helpers are pure HTTP/JSON; JWT inspection is client-side only (expiry/UX),
never a signature check. DCQL is a generic evaluator — scope semantics are the
consuming product’s concern.
Large objects under IBE — ibeSealBlob / ibeOpenBlob
Section titled “Large objects under IBE — ibeSealBlob / ibeOpenBlob”ibeEncrypt is a KEM for a small payload. For an image, a scan series or any multi-megabyte
object, seal an envelope: a fresh 32-byte data key IBE-wrapped to the identity, the body
AES-256-GCM in fixed chunks (WebCrypto, default 1 MiB), each chunk’s nonce
blobId[0..8] ‖ be32(i) and its AAD "keykeeper/ibe-blob/v1" ‖ blobId ‖ be32(i) ‖ last ‖ identity
— so a chunk cannot be reordered, dropped, truncated or replayed under another identity.
import {ibeSealBlob, ibeOpenBlob, ibeUnwrapBlobKey, ibeBlobDecryptKey, ibeDecryptBlobChunk, ibeBlobChunkRange} from 'tasra-sdk'
// producer (offline, nothing but the slot's public MPK)const {header, body} = await ibeSealBlob(mpk, 'did:key:zPatient/imaging/2026-09', pngBytes, {contentType: 'image/png'})// store `body` as a blob and `header` beside it (header.wrappedKey is the IBE ciphertext of the data key)
// consumer, after an identity-scoped extraction gave it sk_ID for that identityconst png = await ibeOpenBlob(skId, header, body) // whole object in memory, or …const dek = ibeUnwrapBlobKey(skId, header) // … stream it:const key = await ibeBlobDecryptKey(dek)for (let i = 0; i < header.chunkCount; i++) { const {start, end} = ibeBlobChunkRange(header, i) // byte range of chunk i in `body` const plain = await ibeDecryptBlobChunk(key, header, i, await fetchRange(start, end))}sk_ID is a per-identity capability, so one extraction opens every object sealed to that
identity; time-box identities (…/2026-09) rather than expecting to revoke one. Use a producer-authenticated manifest containing both the header and the body
digest (ibeBlobDigest(body)) so the consumer can reject substituted metadata or
ciphertext before decrypting. A signed body digest alone does not protect the header.
Chunk size must be an integer of at least 1024 bytes. Empty input still has one
chunk: zero plaintext bytes and a 16-byte authentication tag. Exact chunk-size
multiples add no extra chunk; the last partial chunk contains the remaining bytes.
ibeBlobChunkRange returns an exclusive end; an HTTP Range request ends at
end - 1. Validate safe-integer indices in [0, header.chunkCount) and use a trusted
header. The SDK rejects negative and past-end indices; it does not validate every
malformed header or fractional index. Authenticate the header alongside the body
digest before trusting metadata such as contentType.
tasra-sdk/oid4vp — credential wallets against the Verifier Agent
Section titled “tasra-sdk/oid4vp — credential wallets against the Verifier Agent”The wallet protocol uses OpenID4VP 1.0 with DCQL, SD-JWT VC + KB-JWT, JARM
direct_post.jwt, and OpenID4VCI pre-authorized issuance. This subpath supports
two application roles:
// APP / RELYING PARTY: sign the operation with the slot creator's EVM key (EIP-712// PresentationOperation), open a session on the verifier-agent, hand the openid4vp:// payload to a wallet,// collect the compound token — the credentials never come back here.import {openVerifierAgentSession, awaitVerifierAgentResult} from 'tasra-sdk/oid4vp'const session = await openVerifierAgentSession({verifierAgentUrl, chainId, keyRegistry, slotId, action: 'sign', message, description, signer: creatorAccount})showQr(session.qrPayload)const {token} = await awaitVerifierAgentResult(session) // checks token.request_hash against the session
// WALLET: receive a credential from any OpenID4VCI issuer, then answer a request: fetch + verify// the JAR (did:web → the verifier-agent's set document), plan against its dcql_query, let the user pick ONE// credential, disclose only the claims asked for, bind with a KB-JWT, encrypt, POST.import {randomHolderKey, receiveCredential, presentToRequestUri} from 'tasra-sdk/oid4vp'const holder = randomHolderKey() // P-256 did:jwk, cnf.kid = did#0const {credential} = await receiveCredential({offerUri, holder})await presentToRequestUri(qrPayload, [{sdJwt: credential}], holder, {choose: askUser})planPresentation / buildResponse / submitResponse are the steps behind presentToRequestUri
for a UI that wants a consent screen between them. One credential per presentation: a rule that
needs two credentials must use credential_sets with single-credential options (what the Hovi
Wallet can satisfy). Issuers use issueSdJwtVc (P-256 did:key or did:jwk issuer); the
request binding (requestHash, derivedNonce) lives in binding for the verifier-agent
side. A wallet copies the nonce; it never interprets it.
Bring your own identity provider — OAuth + DPoP against the Verifier Agent
Section titled “Bring your own identity provider — OAuth + DPoP against the Verifier Agent”A slot whose rule uses the oauth+access-token+dpop format is authorized by an access token
your users already get from your own IdP, sender-constrained with DPoP (RFC 9449). The rule is
ordinary OID4VP-DCQL over the token’s own claims — iss pinned, ["aud", null] containing the
platform audience, meta.max_age_secs, plus whatever roles you require:
{"credentials":[{"id":"t","format":"oauth+access-token+dpop","meta":{"max_age_secs":300}, "claims":[ {"path":["iss"],"values":["https://idp.example.com/realms/acme"]}, {"path":["aud",null],"values":["https://agent.tasra.example/authz/43114"]}, {"path":["realm_access","roles",null],"values":["treasury-signer"]}]}]}validateDcql refuses an oauth+* query without the pinned iss, the ["aud", null] entry or
meta.max_age_secs (1–86400). The audience
is derived, never chosen — platformAudience(agentOrigin, chainId) from
tasra-sdk/committee, or read it back from any createOauthSession reply.
import {auth0DpopSigner, createOauthSession, submitOauthResponse, waitForSession} from 'tasra-sdk'import {presentationOperationTypedData} from 'tasra-sdk/oid4vp'
const {typedData, operation, messageHex} = presentationOperationTypedData({ chainId, keyRegistry, slotId, action: 'sign', message, description: 'Treasury payout #42',})const operationSig = await creatorSigner.signTypedData(typedData)
const session = await createOauthSession(verifierAgentUrl, {operation, operationSig, messageHex})// → {sessionId, pollSecret, nonce, dpopHtu, platformAudience} — no QR, no Request Object
await submitOauthResponse(verifierAgentUrl, { sessionId: session.sessionId, pollSecret: session.pollSecret, accessToken, nonce: session.nonce, dpopHtu: session.dpopHtu, signer, // MUST sign with the key the token is bound to})const result = await waitForSession(verifierAgentUrl, session.sessionId, session.pollSecret)if (result.status !== 'done') throw new Error(`refused: ${result.error}`)// result.compoundToken + result.verifierProofs → the keeper's /v1/committee/signThe signer is decided by where your token’s DPoP key lives:
| Your token came from | signer |
|---|---|
auth0-spa-js with useDpop |
auth0DpopSigner((args) => auth0.generateDpopProof(args)) |
oidc-client-ts with dpop enabled |
{proof: ({htm, htu, nonce}) => userManager.dpopProof(htu, user, htm, nonce)} (it computes ath from the user’s token) |
your own token request with a key from createDpopKey() |
key.signer |
createDpopKey().signer mints resource-request proofs only — it always sets ath and
nonce — and the SDK has no token-request proof helper, so it cannot obtain the token from the
IdP for you. Prefer letting your OIDC library own the key.
Sign over session.dpopHtu, not the request URL. The platform’s htu is
{agentOrigin}/v1/sessions/oauth-response (no session id), while the request goes to
/v1/sessions/{id}/oauth-response. submitOauthResponse passes dpopHtu to the signer; a
generic DPoP fetch helper that derives htu from the request URL — including auth0-spa-js’s
fetchWithAuth — is refused.
submitOauthResponse resolving does not mean authorized. The agent accepts the delivery
and runs the committee; a refusal (wrong role, stale token, wrong audience…) surfaces from
waitForSession as status: 'failed' with the verifiers’ reasons in error (it RETURNS a
failed session rather than throwing — check status). submitOauthResponse answers the agent’s use_dpop_nonce challenge once by itself.
What the platform proves this way is PRESENCE, not intent — the user never sees the operation; pin dual control on slots that need intent. Configuring a tenant identity provider is a deployment task, documented by whoever operates the network.
tasra-sdk/committee — protocol internals
Section titled “tasra-sdk/committee — protocol internals”The compound-token layer — committee draw, canonical hashing, ed25519 quorum
verification, verifier-set Merkle proofs, wire codecs, and the
/v1/committee-authorize orchestration — lives behind its own subpath:
import {verifyCompoundToken, selectVerifierCommittee} from 'tasra-sdk/committee'It also carries the derivations platformAudience(origin, chainId) and
dpopHtu(origin) (+ normalizeOrigin, OAUTH_RESPONSE_PATH). Use these helpers
to preserve the deployment protocol’s exact audience and URI binding.
Most consumers never need it. Typed slots on TasraClient handle these
operations. Advanced integrations can use createCommitteeSlotClient from
tasra-sdk/chain; reach for protocol primitives when implementing or auditing the
protocol itself.
These ~35 symbols are deep protocol internals — canonical hashing, Merkle proofs,
the committee draw, wire codecs — and they are not on the main entry, so
import {…} from 'tasra-sdk' autocompletes to the managed surface rather
than to compoundTokenCanonicalBytes.
