Skip to content

Agent Operations

RunTimeAdmin edited this page Nov 3, 2026 · 19 revisions

Agent Operations

**Referenced Files in This Document** - [agent.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/agent.ts) - [keys.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/keys.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) - [verifier.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/verifier.ts) - [types.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/types.ts) - [index.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/index.ts) - [stake.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/stake.ts) - [gate.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/gate.ts) - [agent.test.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/test/agent.test.ts) - [registration-signature.test.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/test/registration-signature.test.ts) - [CHANGELOG.md](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/CHANGELOG.md) - [SigvaraIdentity.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/SigvaraIdentity.sol) - [SigvaraReader.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/SigvaraReader.sol)

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 agent operations in the Sigvara SDK, focusing on:

  • Creating agents with Ed25519 key pairs and managing private keys securely
  • Deriving public keys and DID identifiers for on-chain registration
  • Registering agents on-chain, including digest computation and transaction submission
  • Challenge-response authentication between agents and verifiers
  • Key rotation procedures to update credentials while maintaining continuity
  • Practical lifecycle patterns: creation, registration, updates, and deactivation
  • Security best practices for key storage, signing, and error handling
  • Integration patterns with wallet providers and transaction signing libraries

The SDK exposes a small, focused API surface that keeps cryptographic details internal while providing clear integration points for wallets, signers, and on-chain contracts.

Project Structure

The SDK is organized by capability:

  • Identity and keys: agent identity, DID formatting, and Ed25519 utilities
  • Authentication: challenge generation, signing, parsing, and verification
  • On-chain interaction: verifier reads, reputation checks, and registration transactions
  • Gatekeeping: admission control with replay protection and threshold checks
  • Stake management: operator deposit flows for bonding agents
  • Public exports: a single entry point re-exporting core types and functions
graph TB
subgraph "SDK Core"
A["SigvaraAgent<br/>agent.ts"]
K["Key Utilities<br/>keys.ts"]
D["DID Helpers<br/>did.ts"]
C["Challenge Engine<br/>challenge.ts"]
V["SigvaraVerifier<br/>verifier.ts"]
G["SigvaraGate<br/>gate.ts"]
S["Staking Helper<br/>stake.ts"]
T["Types & Exports<br/>types.ts / index.ts"]
end
A --> K
A --> D
A --> C
V --> D
V --> C
V --> K
G --> V
G --> C
S --> V
T --> A
T --> V
T --> G
T --> S
T --> C
T --> D
T --> K
Loading

Diagram sources

Section sources

Core Components

  • SigvaraAgent: encapsulates an Ed25519 keypair, derives DID and bytes32 public key, issues and signs challenges. It keeps the secret key in a private field, and signChallengeFor(payload, audience) signs only a challenge addressed to the audience the caller passes (both are new in 1.0.0-alpha.10).
  • SigvaraVerifier: reads on-chain identity and reputation, verifies signatures against the registered public key, builds DID documents, and registers agents.
  • SigvaraGate: provides admission control with challenge issuance, replay protection, status checks, and score thresholds.
  • Stake helpers: simplify ERC-20 approvals and staking deposits for operators.
  • Key and DID utilities: safe hex/base58 conversions, Ed25519 seed/keypair derivation, multibase encoding for W3C DID Documents.

Key responsibilities:

  • Agent lifecycle: create, derive identity, register, rotate keys, deactivate via stake or status changes.
  • Authentication: secure challenge-response binding prover, audience, nonce, and timestamp.
  • On-chain integration: EIP-191 signed registration digests, event parsing, and contract interactions.

Section sources

Architecture Overview

The agent architecture separates client-side cryptography from on-chain state and gate policies:

sequenceDiagram
participant Client as "Client App"
participant Agent as "SigvaraAgent"
participant Verifier as "SigvaraVerifier"
participant Chain as "Identity/Staking Contracts"
participant Gate as "SigvaraGate"
Client->>Agent : generate({ agentAddress, chainId })
Agent-->>Client : { agent, privateKey }
Client->>Verifier : registerAgent(signer, agentAddress, publicKeyBytes32, identityAddress, proof)
Verifier->>Chain : compute registrationDigest(...)
Verifier->>Chain : registerAgent(agentAddress, ed25519PubKeyBytes32, signature)
Chain-->>Verifier : emit AgentRegistered(didHash)
Verifier-->>Client : { didHash, txHash }
Client->>Gate : challenge(did)
Gate-->>Client : Challenge(payload, nonce, expiresAt)
Client->>Agent : signChallengeFor(payload, audience)
Agent-->>Client : base58(signature)
Client->>Gate : admit(did, challenge, signature)
Gate->>Verifier : verifySignature(did, payload, signature, ttl, audience)
Verifier->>Chain : getIdentity(did), getTotalScore(did)
Chain-->>Verifier : identity, score
Verifier-->>Gate : verified?
Gate-->>Client : AdmitResult(ok, reason, score)
Loading

Diagram sources

Detailed Component Analysis

Agent Creation and Key Management

  • Ed25519 keypair generation uses a cryptographically random 32-byte seed. The SDK returns both the agent instance and the raw private key (hex) for secure storage.
  • Private key validation enforces exact length and valid hex characters; invalid input throws descriptive errors rather than silently producing incorrect keys.
  • Public key derivation produces:
    • bytes32 hex string for on-chain registration
    • multibase representation for W3C DID Documents
  • DID formatting and hashing mirror on-chain logic, enabling off-chain derivation of the same identifier hash used by contracts.
flowchart TD
Start(["Create Agent"]) --> GenSeed["Generate 32-byte seed"]
GenSeed --> BuildAgent["Construct SigvaraAgent(seed, agentAddress, chainId)"]
BuildAgent --> PubKey["Derive Ed25519 public key"]
PubKey --> Bytes32["Convert to bytes32 hex"]
Bytes32 --> Did["Format DID and compute DID hash"]
Did --> Ready(["Agent ready for registration"])
Loading

Diagram sources

Section sources

Registration Workflow

Registration binds three entities: the agent address, the operator address, and the Ed25519 public key. The SDK computes the registration digest from the contract, signs it with the agent’s EOA signer, and submits the transaction.

Key steps:

  • Compute registration digest using the contract function
  • Sign the raw 32-byte digest (EIP-191 prefix applied by the signer)
  • Submit registerAgent with agent address, bytes32 public key, and signature
  • Parse logs to extract didHash and txHash
sequenceDiagram
participant Operator as "Operator Wallet"
participant Verifier as "registerAgent()"
participant Contract as "Identity Contract"
Operator->>Verifier : call with (agentAddress, pubKeyBytes32, identityAddress, proof)
Verifier->>Contract : registrationDigest(agentAddress, operator, pubKeyBytes32)
Contract-->>Verifier : digest (unprefixed)
Verifier->>Verifier : signMessage(raw digest)
Verifier->>Contract : registerAgent(agentAddress, pubKeyBytes32, signature)
Contract-->>Verifier : emit AgentRegistered(didHash)
Verifier-->>Operator : { didHash, txHash }
Loading

Diagram sources

Section sources

Challenge-Response Authentication

The SDK implements a robust challenge-response protocol:

  • v2 payloads bind prover DID, audience, nonce, and timestamp
  • Audience binding prevents relay attacks across verifiers
  • Nonce ensures single-use within TTL window
  • Signature is base58-encoded Ed25519 over UTF-8 payload
  • The signer can check the audience before signing with signChallengeFor(payload, audience) (1.0.0-alpha.10 and later). The agent's signChallenge, which signed whatever it was handed, is removed in 1.0.0-alpha.12

Verification flow:

  • Parse and validate payload structure
  • Check prover DID matches expected agent
  • Require the expected audience, which a call cannot leave out, and enforce audience binding
  • Reject expired challenges, and ones dated more than 60 seconds ahead of the verifier’s clock (MAX_FUTURE_SKEW_SECONDS)
  • Validate signature width and decode safely
  • Fetch on-chain public key and verify signature
flowchart TD
Start(["Verify Signature"]) --> Parse["Parse challenge payload"]
Parse --> CheckDid{"Prover DID matches?"}
CheckDid --> |No| FailBadProof["Return bad_proof"]
CheckDid --> |Yes| CheckAudience{"Audience bound and correct?"}
CheckAudience --> |No| FailBadProof
CheckAudience --> |Yes| CheckExpiry{"Not expired?"}
CheckExpiry --> |No| FailBadProof
CheckExpiry --> |Yes| WidthCheck["Validate signature length"]
WidthCheck --> Decode["Decode base58 signature"]
Decode --> FetchKey["Fetch on-chain public key"]
FetchKey --> Verify["Verify Ed25519 signature"]
Verify --> Result(["true/false"])
Loading

Diagram sources

Section sources

Gate Admission Control

SigvaraGate orchestrates admission decisions:

  • Issues challenges bound to a stable audience
  • Verifies signatures and enforces replay protection via nonces
  • Checks agent activity status and reputation threshold
  • Returns explicit reasons for refusal to aid debugging
  • Reads status, the bond held now and the score in parallel (standing is still named before score in the answer), and refuses to be built with a threshold that is not a finite number of 0 or more, or a ttlSeconds or maxScoreAgeSeconds that is not positive
  • Compares the spendable score and checks the stored status and the bond held now against the current minimum stake, as SigvaraReader.isTrusted does for an on-chain consumer (alpha.9 does not check the bond). With maxScoreAgeSeconds it also refuses a score nobody has refreshed

Nonce store:

  • In-memory default for single-process deployments
  • Optional distributed store with atomic consume for fleet safety. atomicNonceStore builds one from a Redis SET NX or a database insert, and instances: 'multiple' makes the gate refuse to start without one (1.0.0-alpha.13)
  • profile: 'production' (1.0.0-alpha.14) makes the gate refuse to start until instances and maxScoreAgeSeconds are both set
  • Pruning strategy bounds memory usage by TTL
classDiagram
class SigvaraGate {
+challenge(did) Challenge
+admit(did, challenge, signature) AdmitResult
-consumeNonce(nonce, expiresAt, nowSeconds) boolean
-verifier : SigvaraVerifier
-nonces : NonceStore
-ttl : number
-threshold : number
-audience : string
}
class MemoryNonceStore {
+seen(nonce) boolean
+add(nonce, expiresAt) void
+prune(nowSeconds) void
+consume(nonce, expiresAt) boolean
}
class SigvaraVerifier {
+verifySignature(did, payload, signature, maxAge, expectedAudience) boolean
+isActive(did) boolean
+getTotalScore(did) number
}
SigvaraGate --> SigvaraVerifier : "uses"
SigvaraGate --> MemoryNonceStore : "owns"
Loading

Diagram sources

Section sources

Stake and Bonding

Operators deposit stake to activate agents:

  • Approve token allowance if needed
  • Deposit stake for the agent’s didHash
  • Return transaction hashes for approval and deposit
flowchart TD
Start(["Deposit Stake"]) --> CheckAllowance["Check token allowance"]
CheckAllowance --> NeedApprove{"Allowance < amount?"}
NeedApprove --> |Yes| Approve["token.approve(staking, amount)"]
NeedApprove --> |No| Deposit["staking.depositStake(didHash, amount)"]
Approve --> Deposit
Deposit --> Done(["txHash, approveTxHash?"])
Loading

Diagram sources

Section sources

Key Rotation Procedures

To rotate agent credentials:

  • Generate a new Ed25519 keypair and retain the new private key securely
  • Derive the new bytes32 public key
  • Call rotatePublicKey(didHash, newEd25519PubKey) on SigvaraIdentity from the operator wallet. The SDK carries the ABI entry but no helper, and registerAgent cannot change a key: a didHash registers once, so a second registration reverts with AlreadyRegistered
  • Keep using the old key until the rotation transaction is confirmed
  • Update any stored references to the agent’s public key and DID

Operational notes:

  • The DID hash is immutable per agent address and chain; only the public key changes
  • Keep the operator signer consistent unless also rotating operator control
  • After rotation, verify on-chain identity reflects the new public key before relying on it
  • Suspended agents may rotate, so after a suspected compromise the operator can suspend the agent first (updateStatus(didHash, Suspended)), rotate, and then reinstate it. Verifiers that check status (isActive, SigvaraGate) refuse the agent meanwhile, and SigvaraVerifier.verifySignature alone does not check status. Slashed agents cannot rotate

Section sources

Deactivation and Status Changes

Agents can become inactive through:

  • Suspension or slashing by governance mechanisms
  • Insufficient stake causing PendingBond or non-active status
  • Explicit deactivation flows defined by on-chain contracts

Integration guidance:

  • Always check isActive before admitting work. It reads the stored status; the gate also checks the bond held now, and an on-chain consumer that wants that plus the score age uses SigvaraReader (isTrusted, isTrustedFresh)
  • Use gate admission results to distinguish bad proofs, replays, not active, stale scores (if you set maxScoreAgeSeconds) and below threshold
  • Monitor stake levels and reputation scores to proactively maintain Active status

Section sources

Dependency Analysis

The SDK exhibits low coupling and high cohesion:

  • SigvaraAgent depends on keys, did, and challenge modules
  • SigvaraVerifier depends on did, challenge, keys, and contract ABIs
  • SigvaraGate composes SigvaraVerifier and a pluggable NonceStore
  • Stake helper depends on contract ABIs and ethers
graph LR
Agent["agent.ts"] --> Keys["keys.ts"]
Agent --> Did["did.ts"]
Agent --> Challenge["challenge.ts"]
Verifier["verifier.ts"] --> Did
Verifier --> Challenge
Verifier --> Keys
Gate["gate.ts"] --> Verifier
Gate --> Challenge
Stake["stake.ts"] --> Verifier
Loading

Diagram sources

Section sources

Performance Considerations

  • Lazy contract instantiation in SigvaraVerifier avoids repeated ABI parsing overhead
  • Signature width checks prevent expensive decoding of malformed inputs
  • Reputation total score read is optimized to avoid unnecessary tuple decoding
  • Nonce pruning in MemoryNonceStore runs proportional to expired entries, not live set size
  • Challenge TTLs should be tuned to balance security and latency
  • Chain reads give up after 30 seconds by default (timeoutMs on SigvaraVerifier and SigvaraGate, where ethers alone waits five minutes), and SigvaraGate.admit reads status, bond and score together, so an admit costs two round trips rather than three

Troubleshooting Guide

Common issues and resolutions:

  • Invalid hex private key: ensure exactly 64 hex characters; the SDK throws precise errors indicating the offending character and position
  • Double-prefixed registration digest: sign the raw 32-byte digest returned by the contract; do not pre-hash or re-prefix
  • Replay detection: implement nonce tracking at the gate; use a distributed NonceStore for multi-instance deployments
  • Audience mismatch: always include a stable audience in challenges; verifiers will reject payloads without matching audience
  • Expired challenges: enforce TTL checks and prompt clients to request fresh challenges. A challenge dated more than 60 seconds ahead of the verifier’s clock is refused as not fresh too
  • Base58 signature errors: catch decode exceptions and treat them as failed proofs rather than server errors

Section sources

Conclusion

The Sigvara SDK provides a secure, well-structured foundation for agent operations:

  • Clear separation of cryptographic primitives, identity management, and on-chain interactions
  • Robust challenge-response authentication with audience binding and replay protection
  • Straightforward registration and stake workflows compatible with standard wallet providers
  • Extensible gatekeeping with explicit admission reasons and scalable nonce stores

Adopt these patterns to build reliable agent systems with strong security guarantees and clear operational visibility.

Appendices

Practical Code Examples (by reference)

Section sources

Overview

Clone this wiki locally