Skip to content

TypeScript SDK

RunTimeAdmin edited this page Nov 5, 2026 · 35 revisions

TypeScript SDK

**Referenced Files in This Document** - [standing.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/standing.ts) - [x402.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/x402.ts) - [tools.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/tools.ts) - [index.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/index.ts) - [agent.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/agent.ts) - [verifier.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/verifier.ts) - [gate.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/gate.ts) - [challenge.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/challenge.ts) - [did.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/did.ts) - [keys.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/keys.ts) - [stake.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/stake.ts) - [abis.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/abis.ts) - [types.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/types.ts) - [README.md](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/README.md)

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion
  10. Appendices

Introduction

This document explains Sigvara’s TypeScript SDK for agent identity, cryptographic authentication, reputation checks, stake management, and gate-based access control. It covers:

  • Agent creation with Ed25519 keys and DID derivation
  • Challenge-response authentication and signature verification
  • Verifier functions for reputation and threshold validation
  • Gate-based admission with nonce management and detailed refusal reasons
  • A check on a bare address before paying or hiring it, under an x402 client or in an agent framework's tools
  • Stake deposit automation
  • Integration patterns for web apps, backend services, and smart contract interactions
  • Troubleshooting guidance for cryptography and network issues

The SDK is modular: agent operations, verification utilities, and access control are separated into focused modules with a clean public API surface.

This page describes the SDK in 1.0.0-alpha.16, published to npm on 5 November 2026. New since 1.0.0-alpha.15: SigvaraTrustCheck, which asks the gate's question about a bare address, with no proof; sigvaraX402Fetch, which checks the seller before an x402 payment is signed; sigvaraTrustTool and requireTrustedAgent, for LangChain and its like; minBond on SigvaraGate, with its below_bond refusal; challengeSecret on SigvaraGate, which accepts only challenges the gate issued, with generateChallenge taking an optional fourth argument that makes the nonce; a spent nonce recorded for one agent and one audience, as a hash a NonceStore is handed where it was handed the nonce; and SigvaraVerifier keeping to 20 calls in a batch. New since 1.0.0-alpha.14: sigvara-mcp, a read-only MCP trust server for an agent's MCP host (see MCP Gated Tools), the evidence-root functions evidenceLeaf, evidenceRoot and matchEvidenceRoot, and reads on SigvaraVerifier for a trust check, verifyEvidence and getEvidenceRoot among them. New since 1.0.0-alpha.13: profile: 'production' on SigvaraGate, which makes the constructor refuse to build the gate until instances and maxScoreAgeSeconds are both set. New since 1.0.0-alpha.12: atomicNonceStore; the gate's instances setting, with which a gate set to 'multiple' refuses to start without a nonce store with an atomic consume(); and signPaymentOutcome and verifyPaymentOutcome, which sign and check a payer's statement of whether one payment's work was delivered. New since 1.0.0-alpha.11: the agent's signChallenge is removed, so a call does not compile in TypeScript and throws in JavaScript, and signChallengeFor is the way to sign. New since 1.0.0-alpha.10: verifySignature requires the expected audience (see the changelog). These items are new since 1.0.0-alpha.9 (listed in the changelog): signChallengeFor, the 30 second read timeout, the parallel status and score read, the gate also checking the bond held now, the optional maxScoreAgeSeconds limit and its stale_score refusal, the refusal of a challenge dated in the future, the checks on a gate's threshold, ttlSeconds and maxScoreAgeSeconds, and the agent keeping its secret key in a private field.

Project Structure

The SDK lives under packages/sdk/src and exposes a cohesive API through its index. The main modules are:

  • Public entry point that re-exports the stable API
  • Agent operations for key generation, DID helpers, and challenge signing
  • Verifier for on-chain reads and signature verification
  • Gate for admission decisions with replay protection
  • Cryptographic primitives and encoding utilities
  • DID formatting and hashing
  • Challenge payload generation, parsing, and expiry checks
  • Stake deposit helper
  • Evidence roots: leaves, trees, proofs and root matching (from 1.0.0-alpha.15)
  • sigvara-mcp, a read-only MCP trust server, in dist/mcp and as a bin (from 1.0.0-alpha.15)
  • Standing: standingDecision, the one rule for the gate, sigvara-mcp and the trust check, and SigvaraTrustCheck (from 1.0.0-alpha.16)
  • x402: sigvaraX402Fetch, a check on the seller before a payment is signed (from 1.0.0-alpha.16)
  • Framework tools: sigvaraTrustTool and requireTrustedAgent (from 1.0.0-alpha.16)
  • Contract ABIs and shared types
graph TB
Index["index.ts<br/>Public API"] --> Agent["agent.ts<br/>SigvaraAgent"]
Index --> Verifier["verifier.ts<br/>SigvaraVerifier + registerAgent"]
Index --> Gate["gate.ts<br/>SigvaraGate"]
Index --> Standing["standing.ts<br/>SigvaraTrustCheck + standingDecision"]
Index --> X402["x402.ts<br/>sigvaraX402Fetch"]
Index --> Tools["tools.ts<br/>sigvaraTrustTool + requireTrustedAgent"]
Gate --> Standing
Standing --> Verifier
Index --> Challenge["challenge.ts<br/>generate/sign/verify/parse"]
Index --> Did["did.ts<br/>format/parse/hash"]
Index --> Keys["keys.ts<br/>base58/hex/Ed25519 helpers"]
Index --> Stake["stake.ts<br/>depositStake"]
Verifier --> Abis["abis.ts<br/>Identity/Reputation/Staking ERC20"]
Verifier --> Types["types.ts<br/>shared types"]
Gate --> Verifier
Agent --> Challenge
Agent --> Did
Agent --> Keys
Loading

Diagram sources

Section sources

Core Components

  • SigvaraAgent: Creates Ed25519 keypairs, derives DIDs and hashes, issues challenges, and signs payloads.
  • SigvaraVerifier: Reads on-chain identity, reputation, and stake; verifies signatures against chain-published keys; builds DID Documents.
  • SigvaraGate: Issues challenges, enforces replay protection via nonces, checks agent activity and reputation thresholds, and returns structured admission results.
  • SigvaraTrustCheck: Asks the gate's question about a bare address, with no proof, for "should I pay or hire this" (from 1.0.0-alpha.16).
  • sigvaraX402Fetch: Goes underneath an x402 client and refuses a seller without standing before anything is signed (from 1.0.0-alpha.16).
  • sigvaraTrustTool and requireTrustedAgent: Put the same check in an agent framework's tools, with no framework as a dependency (from 1.0.0-alpha.16).
  • Challenge utilities: Generate v2 payloads with audience binding, sign/verify Ed25519 signatures, parse payloads, and check expiry.
  • DID utilities: Format, parse, and hash DIDs consistently with on-chain logic.
  • Key utilities: Base58 encode/decode, hex conversion, seed-to-keypair, bytes32 conversions, and multibase encoding for DID Documents.
  • Stake helper: Approve token allowance and deposit stake in one flow.
  • Payment outcomes: signPaymentOutcome signs, as an agent's payer, whether one payment's work was delivered, over EIP-712 typed data, and verifyPaymentOutcome checks such a signature the way the oracle does (from 1.0.0-alpha.13).

Key responsibilities:

  • Agent operations: key generation, DID derivation, challenge issuance and signing
  • Verification: on-chain reads, signature verification, DID Document construction
  • Access control: admission decision with clear refusal reasons and nonce management
  • Utilities: cryptography, encoding, and ABI/type definitions

Section sources

Architecture Overview

The SDK separates concerns across modules and integrates with Ethereum contracts via ethers. Agents generate keys and DIDs, verifiers read state and verify signatures, and gates enforce policy using verified proofs and on-chain reputation.

sequenceDiagram
participant Client as "Client"
participant Agent as "SigvaraAgent"
participant Verifier as "SigvaraVerifier"
participant Gate as "SigvaraGate"
participant Chain as "On-chain Contracts"
Client->>Agent : generate({ agentAddress, chainId })
Agent-->>Client : { agent, privateKey }
Client->>Verifier : registerAgent(operatorSigner, agentAddress, pubKeyBytes32, identityAddr, proof)
Verifier->>Chain : registerAgent(...)
Chain-->>Verifier : event AgentRegistered(didHash)
Verifier-->>Client : { didHash, txHash }
Client->>Gate : challenge(agentDid)
Gate-->>Client : Challenge(payload, nonce, timestamp, expiresAt)
Client->>Agent : signChallengeFor(challenge.payload, audience)
Agent-->>Client : signatureBase58
Client->>Gate : admit(agentDid, challenge, signatureBase58)
Gate->>Verifier : verifySignature(did, payload, sig, ttl, audience)
Verifier->>Chain : getIdentity(didHash), isActive(didHash), hasMinimumStake(didHash), getTotalScore(didHash)
Chain-->>Verifier : identity, status, score
Verifier-->>Gate : verified
Gate-->>Client : AdmitResult(ok, reason, score, threshold, did)
Loading

Diagram sources

Detailed Component Analysis

Agent Operations (SigvaraAgent)

Responsibilities:

  • Generate secure Ed25519 keypairs from random seeds
  • Derive DID and DID hash consistent with on-chain logic
  • Issue challenges bound to a peer DID and an audience
  • Sign challenge payloads with the agent’s private key

Key behaviors:

  • Static factory generates a new agent and returns the raw private key for secure storage
  • DID and hash are derived deterministically from agent address and chain ID
  • Challenges include prover DID, audience, nonce, and timestamp
  • Signing uses Ed25519 over UTF-8 payload and returns base58-encoded signatures
  • signChallengeFor(payload, audience) signs only a v2 challenge that names this agent as the prover and names exactly the audience the caller passes, and otherwise throws and signs nothing. The audience in a challenge protects an agent from relay only if the agent checks it before signing, so the agent's blind signChallenge(payload) is removed in 1.0.0-alpha.12 (on alpha.9 only signChallenge exists, so check the payload's audience yourself)
  • The Ed25519 secret key is held in a true private field, so JSON.stringify(agent), a logged agent and an error reporter's snapshot do not carry it (on alpha.9 the key is an ordinary property and they do; the key of such an agent that was ever logged or serialized should be treated as exposed and rotated, see Key Management and Rotation)
classDiagram
class SigvaraAgent {
-keyPair
-_agentAddress
-_chainId
-_challengeTtl
+constructor(options)
+static generate(options)
+agentAddress
+did
+didHash
+publicKeyBytes32
+issueChallenge(peerDid, ttlSeconds)
+signChallengeFor(payload, audience)
}
Loading

Diagram sources

Section sources

Verifier Functions (SigvaraVerifier)

Responsibilities:

  • Read agent identity, status, reputation factors and total score
  • Check minimum stake and threshold compliance
  • Verify challenge-response signatures against on-chain public keys
  • Build W3C-compliant DID Documents
  • Read what a trust check needs (from 1.0.0-alpha.15): earned score, pending score, minimum stake, a queued withdrawal, the last slash proposal, the evidence root, and verifyEvidence for one payment

Security considerations:

  • Audience binding prevents relay attacks: expectedAudience is required (1.0.0-alpha.10 and earlier let a caller leave it out, which skipped the check), and a v1 payload, which names no audience, is refused
  • Nonce freshness is enforced by TTL; integrators must track nonces if not using the gate
  • Signature length and alphabet validation prevent malformed inputs from causing errors or crashes
  • A challenge dated more than 60 seconds (MAX_FUTURE_SKEW_SECONDS) ahead of the verifier's clock is refused as not fresh. Before this fix a payload dated ahead counted as fresh for as long as the date stayed ahead (alpha.9 still does)
  • Chain reads give up after 30 seconds (DEFAULT_RPC_TIMEOUT_MS), and timeoutMs on SigvaraVerifier and SigvaraGate changes it. ethers waits five minutes by default, which is what alpha.9 does
  • The provider keeps to 20 calls in a JSON-RPC batch (batchMaxCount): ethers folds what is asked in one tick into one batch of up to 100 calls, and Arc's public RPC answers 20 and refuses the rest with a rate limit (verifier.ts:44-46)
flowchart TD
Start([verifySignature]) --> AudienceGiven{"Expected audience given?"}
AudienceGiven --> |No| ThrowMissing["Throw: expectedAudience is required"]
AudienceGiven --> |Yes| Parse["Parse challenge payload"]
Parse --> MatchDid{"Payload DID matches?"}
MatchDid --> |No| ReturnFalse["Return false"]
MatchDid --> |Yes| VersionAudience{"Version 2 and audience match?"}
VersionAudience --> |No| ReturnFalse
VersionAudience --> |Yes| ExpiryCheck["Check expiry"]
ExpiryCheck --> LengthCheck["Validate base58 signature length"]
LengthCheck --> FetchIdentity["Read identity from chain"]
FetchIdentity --> ValidIdentity{"Registered and has pubkey?"}
ValidIdentity --> |No| ReturnFalse
ValidIdentity --> |Yes| CryptoVerify["Verify Ed25519 signature"]
CryptoVerify --> Result{"Verified?"}
Result --> |Yes| True(["Return true"])
Result --> |No| False(["Return false"])
Loading

Diagram sources

Section sources

Gate-Based Access Control (SigvaraGate)

Responsibilities:

  • Issue signed challenges with audience binding
  • Enforce replay protection via nonce store
  • Validate signatures, agent activity, and reputation thresholds
  • Return structured admission results with explicit refusal reasons
  • Refuse to be built with a threshold that is not a finite number of 0 or more, or a ttlSeconds or maxScoreAgeSeconds that is not a positive number. A NaN threshold, for example Number(process.env.X) with X unset, would otherwise admit every active agent that can prove its key
  • Refuse to start as instances: 'multiple' without a nonceStore whose consume() is atomic, and, with profile: 'production', until instances and maxScoreAgeSeconds are both set. Any other profile value is refused rather than ignored
  • Refuse an agent holding less than an optional minBond, in the bond token's base units, as a bigint or a string of digits. A number is refused when the gate is built, because a floor past what a double can hold would quietly become another floor. It compares the same number as SigvaraReader.isTrusted(agent, minScore, minBond), and makes the extra read only when set
  • With challengeSecret (at least 32 characters), accept only challenges this gate issued, with no state shared between instances, and refuse any other with bad_proof before any read

Order of work in admit: the proof is checked first, the nonce is spent second, and the status, the bond held now and the score are read last. Those reads do not depend on each other and go out together, so an admit costs two round trips and not three (alpha.9 reads the status and the score one after the other, and does not read the bond). Standing is named before score. If minBond is set, the stake is read in the same batch and an agent holding less is refused as below_bond, after not_active and before stale_score. If maxScoreAgeSeconds is set, the time the score was proposed is read in the same batch and a score older than the limit, or never written, is refused as stale_score before the number. That age counts from the proposal, so choose more than two of your oracle's proposals apart: a day on Arc testnet and three days on a daily epoch (see Gate Access Control). The rule, in this order, is standingDecision in standing.ts. Checking the proof costs one read of the agent's registered key, because the key lives on chain, so rate-limit callers where the gate is exposed.

Nonce management:

  • Default in-memory store suitable for single-process deployments
  • Optional atomic consume interface for distributed fleets. atomicNonceStore(setIfAbsent) builds such a store from any atomic insert-if-absent, such as a Redis SET NX, and instances: 'multiple' makes the gate refuse to start without one (both from 1.0.0-alpha.13). instances defaults to 'single'
  • Pruning strategy bounded by issue rate and TTL
  • A spent nonce is recorded for one agent and one audience: the store is handed a hash of agent, audience and nonce, read from the signed bytes (from 1.0.0-alpha.16). Nothing about a store needs to change

Refusal reasons:

  • bad_proof: invalid signature or mismatched fields
  • replayed: valid signature already spent
  • not_active: agent exists but is unbonded (or bonded below the minimum stake in force now), suspended, or slashed
  • below_bond: only with minBond: active and holding the protocol's minimum, but less than this gate asks for
  • stale_score: only with maxScoreAgeSeconds: the score was proposed longer ago than the limit, or never
  • below_threshold: active and proven but score insufficient
flowchart TD
A([admit]) --> ParseSigned["Parse signed payload for nonce/timestamp"]
ParseSigned --> Issued{"Issued here? Only if challengeSecret is set"}
Issued --> |No| BadProof
Issued --> |Yes| Verify["Verifier.verifySignature"]
Verify --> Proved{"Proof valid?"}
Proved --> |No| BadProof["Return bad_proof"]
Proved --> |Yes| ComputeExpiry["Compute expiresAt from signed timestamp + TTL"]
ComputeExpiry --> ConsumeNonce["consumeNonce(hash of agent, audience and nonce, expiresAt)"]
ConsumeNonce --> Spent{"Nonce already spent?"}
Spent --> |Yes| Replayed["Return replayed"]
Spent --> |No| IsActive["Verifier.isActive and hasMinimumStake"]
IsActive --> Active{"Active and bonded?"}
Active --> |No| NotActive["Return not_active"]
Active --> |Yes| GetScore["Verifier.getTotalScore"]
GetScore --> Bond{"Holds less than minBond? Only if minBond is set"}
Bond --> |Yes| BelowBond["Return below_bond with score"]
Bond --> |No| Fresh{"Too old? Only if maxScoreAgeSeconds is set"}
Fresh --> |Yes| Stale["Return stale_score with score"]
Fresh --> |No| Threshold{"score >= threshold?"}
Threshold --> |No| Below["Return below_threshold with score"]
Threshold --> |Yes| Admit["Return ok=true with score"]
Loading

Diagram sources

Section sources

Before Paying or Hiring: SigvaraTrustCheck

The gate asks a caller to prove who it is. To decide whether to pay or hire an address, what matters is who the money goes to, so SigvaraTrustCheck (from 1.0.0-alpha.16) asks the gate's question about a bare address, with no proof. It shares standingDecision with the gate and sigvara-mcp, so they cannot come to different answers, and the order is the same: not_active, below_bond, stale_score, below_threshold.

  • Config: rpcUrl, addresses, chainId and minScore, with optional minBond, maxScoreAgeSeconds and timeoutMs.
  • check(agent) returns { ok, agent, reason?, detail?, score? }. not_an_address is one more reason, for anything that is not a 0x address, and detail says what the reason means, for a person or a model.
  • A read that fails throws, with URLs removed. It says nothing about the agent, so it is neither a yes nor a no, and whatever the check guards should not go ahead.
  • It says what the chain holds about that address, and does not prove that the party you are talking to controls it. For that, use SigvaraGate.

sigvaraX402Fetch(fetch, trust) goes underneath an x402 client, so it sees the 402 first: wrapFetchWithPayment(sigvaraX402Fetch(fetch, trust), client).

  • Every payTo offered in the v2 PAYMENT-REQUIRED header and the v1 JSON body must have standing, because the client chooses among them. Otherwise the request rejects with X402SellerRefused, whose refused lists each verdict, and the client signs nothing.
  • A 402 it cannot read is refused. Anything else, including a 402 whose sellers all pass, comes back exactly as it arrived.
  • It checks who is paid, not that work will be delivered. A seller paid anywhere but the agent's registered address reads as having no standing, and is refused.

sigvaraTrustTool(tool, trust) and requireTrustedAgent(fn, trust, agentFrom) put the check in an agent framework's tools. sigvaraTrustTool takes LangChain's tool from you, and requireTrustedAgent wraps a function of yours that you then hand to tool, so LangChain is not a dependency of the SDK.

  • sigvaraTrustTool makes a tool the model can call. It answers in JSON: ok, and reason and detail when it is false.
  • requireTrustedAgent makes a function of yours run only for an agent with standing, and otherwise throw UntrustedAgentError, whose verdict says why. A chain read that fails throws too, and the function does not run. It is the one to rely on: a model can be talked out of calling a check, and cannot be talked out of a refusal in the code it calls.

The real LangChain package and x402 clients were run against this release. AI Framework Integration shows where these go in a LangChain agent.

Section sources

Cryptographic Functions and Encoding

  • Base58 encode/decode with strict alphabet validation
  • Hex conversion with robust error reporting for invalid characters
  • Seed-to-keypair derivation for Ed25519
  • Bytes32 <-> public key conversions for on-chain compatibility
  • Multibase encoding for DID Documents (Ed25519VerificationKey2020)

Complexity notes:

  • Base58 encode/decode operate linearly in input size
  • Hex conversion validates and processes each byte once
  • All cryptographic operations delegate to tweetnacl

Section sources

DID Utilities

  • Parse and format DIDs following the did:sigvara scheme
  • Compute DID hash matching on-chain keccak256 packing

Section sources

Challenge Protocol

  • v2 payloads bind prover DID, audience, nonce, and timestamp
  • v1 payloads still parse, but verifySignature refuses them because they name no audience
  • Newline-delimited format avoids ambiguity with colon-bearing fields
  • generateChallenge takes an optional fourth argument that makes the nonce, which is how a gate with challengeSecret marks the challenges it issues (from 1.0.0-alpha.16)
  • Expiry checks treat unparseable payloads as expired
  • Expiry checks also refuse a payload dated more than 60 seconds ahead of the verifier's clock (MAX_FUTURE_SKEW_SECONDS), so a signature over a far-future timestamp cannot stay valid for years. Challenges a gate issues are dated by its own clock and are unaffected

Section sources

Stake Management

  • depositStake automates ERC-20 approval shortfall and deposits stake
  • Returns transaction hashes for both approval and deposit when applicable

Section sources

Dependency Analysis

The SDK exhibits clear separation of concerns:

  • index.ts re-exports only the public API
  • agent.ts depends on keys, did, and challenge
  • verifier.ts depends on abis, did, challenge, keys, and types
  • gate.ts depends on verifier, challenge and standing
  • standing.ts depends on verifier and did
  • x402.ts and tools.ts take a trust check as an argument and import only its types from standing
  • stake.ts depends on abis
  • challenge.ts depends on keys and types
  • did.ts depends on ethers
  • keys.ts depends on tweetnacl
graph LR
Index["index.ts"] --> Agent["agent.ts"]
Index --> Verifier["verifier.ts"]
Index --> Gate["gate.ts"]
Index --> Challenge["challenge.ts"]
Index --> Did["did.ts"]
Index --> Keys["keys.ts"]
Index --> Stake["stake.ts"]
Index --> Standing["standing.ts"]
Index --> X402["x402.ts"]
Index --> Tools["tools.ts"]
Gate --> Standing
Standing --> Verifier
Standing --> Did
Agent --> Keys
Agent --> Did
Agent --> Challenge
Verifier --> Abis["abis.ts"]
Verifier --> Did
Verifier --> Challenge
Verifier --> Keys
Verifier --> Types["types.ts"]
Gate --> Verifier
Gate --> Challenge
Stake --> Abis
Loading

Diagram sources

Section sources

Performance Considerations

  • Lazy contract instantiation in SigvaraVerifier reduces repeated ABI parsing overhead
  • getTotalScore is used instead of full reputation tuple when only the matured score is needed, avoiding extra contract reads
  • MemoryNonceStore prunes entries proportionally to expired items, keeping memory bounded by issue rate and TTL
  • Signature length checks before chain reads reduce unnecessary RPC calls
  • Challenge parsing treats malformed payloads as expired to fail fast
  • SigvaraGate.admit reads the status, the bond and the score together, and each chain read gives up after 30 seconds by default
  • minBond and maxScoreAgeSeconds each cost one more read per admission, and only when set

Troubleshooting Guide

Common issues and resolutions:

  • Invalid base58 signature: ensure signatures are produced by signChallengeFor and transmitted unchanged; verify character set and length
  • Mismatched audience: pass the same audience string to both generateChallenge and verifySignature; verifySignature throws without it, and refuses a v1 payload, which names no audience
  • Expired challenges: ensure clients sign promptly; adjust TTL appropriately for your environment
  • Replay attempts: use SigvaraGate or implement a shared nonce store with atomic consume semantics
  • Unregistered or inactive agents: confirm registration and stake deposit; check status and minimum stake
  • Network connectivity: verify rpcUrl and chainId; handle provider errors gracefully. A read that gets no answer fails after 30 seconds (set timeoutMs to change it) and after ethers' default of five minutes on alpha.9
  • Hex seed errors: validate hex strings and length; use SigvaraAgent.generate to avoid manual mistakes
  • signChallengeFor throws and signs nothing: the payload is not a v2 Sigvara challenge, names a different prover, or is addressed to a different audience than the one you passed
  • new SigvaraGate(...) throws: threshold is not a finite number of 0 or more, or ttlSeconds or maxScoreAgeSeconds is not a positive number; instances is neither 'single' nor 'multiple', or is 'multiple' with a nonceStore that has no consume(); profile is set to anything but 'production', or is 'production' while instances or maxScoreAgeSeconds is missing (the message names each one missing); minBond is not a bigint or a string of digits; or challengeSecret is shorter than 32 characters
  • X402SellerRefused from a request: a payTo offered has no standing, or the 402 could not be read, in which case refused is empty. A seller has to be paid at the agent's registered address for the check to find it
  • SigvaraTrustCheck.check throws: the chain could not be read, which says nothing about the agent. Do not go ahead, and do not treat it as a refusal of that agent

Operational tips:

  • For multi-instance deployments, provide a NonceStore backed by Redis or database with atomic insert-if-absent, such as atomicNonceStore over a SET NX, and set instances: 'multiple' so the gate refuses to start without it. In a deployment, profile: 'production' makes the gate refuse to start until instances and maxScoreAgeSeconds are set
  • Log AdmitReason to distinguish cryptographic failures from policy denials
  • Use buildDidDocument to publish verification methods for standard resolvers

Section sources

Conclusion

Sigvara’s TypeScript SDK provides a secure, modular toolkit for agent identity, cryptographic authentication, reputation evaluation, stake management, and policy-driven access control. By separating agent operations, verification, and access control, it enables clear integration patterns for web applications, backend services, and smart contract interactions while enforcing strong security properties such as audience binding, nonce replay protection, and on-chain consistency.

Appendices

Practical Examples and Integration Patterns

  • Agent registration and bonding:
    • Generate agent keys and derive DID
    • Register agent with operator signer and agent proof
    • Deposit stake to activate the agent
  • Challenge-response authentication:
    • Issuer creates a challenge with audience
    • Agent signs the payload
    • Verifier checks signature, activity, and threshold
  • Gate-based admission:
    • Use SigvaraGate to issue challenges and decide admission
    • Handle every refusal reason distinctly (below_bond and stale_score too, if you set minBond and maxScoreAgeSeconds)
  • Before paying or hiring an address:
    • Use SigvaraTrustCheck, under an x402 client with sigvaraX402Fetch or in a framework's tools with requireTrustedAgent
  • Web application integration:
    • Frontend collects challenge and signature
    • Backend verifies via SigvaraGate or SigvaraVerifier
    • Store nonces securely if bypassing the gate
  • Backend service integration:
    • Run SigvaraGate behind load balancers with a shared NonceStore
    • Cache verifier instances per process
  • Smart contract interactions:
    • Use registerAgent and depositStake for lifecycle management
    • Reference ABIs and addresses from deployment artifacts

For step-by-step examples, see the SDK README quick start and gate usage sections.

Section sources

Overview

Clone this wiki locally