Skip to content

Agent Registration Workflow

RunTimeAdmin edited this page Oct 29, 2026 · 12 revisions

Agent Registration Workflow

**Referenced Files in This Document** - [did.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/did.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) - [SigvaraIdentity.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/SigvaraIdentity.sol) - [InflowGuard.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/InflowGuard.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

Introduction

This document explains the complete agent registration workflow in Sigvara, from agent initialization to successful on-chain registration. It covers:

  • DID generation using formatDid() and computeDidHash().
  • Signature generation required for registration.
  • Smart contract integration through registerAgent().
  • Step-by-step examples showing agent creation, DID computation, signature generation, and transaction submission.
  • Common failures, gas optimization strategies, and error handling patterns.
  • The relationship between agent addresses, DIDs, and on-chain identity records.

The workflow is implemented across TypeScript SDK utilities and a Solidity identity registry. Clients create an agent with an Ed25519 keypair, compute a deterministic DID and its hash, sign a registration digest with the agent’s Ethereum address, and submit a transaction that registers the agent as PendingBond.

Project Structure

The relevant implementation spans two layers:

  • TypeScript SDK layer: DID formatting, agent abstraction, and registration helper.
  • On-chain Solidity layer: identity registry, DID derivation, signature verification, and state management.
graph TB
subgraph "TypeScript SDK"
did["did.ts<br/>formatDid(), computeDidHash()"]
agent["agent.ts<br/>SigvaraAgent"]
verifier["verifier.ts<br/>registerAgent()"]
end
subgraph "On-chain Identity Registry"
identity["SigvaraIdentity.sol<br/>registerAgent(), computeDidHash()<br/>registrationDigest(), verifyRegistration()"]
end
did --> agent
agent --> verifier
verifier --> identity
Loading

Diagram sources

Section sources

Core Components

  • DID utilities:
    • formatDid(agentAddress, chainId) produces the canonical DID string.
    • computeDidHash(agentAddress, chainId) computes the keccak256 hash used as the primary index.
  • Agent abstraction:
    • SigvaraAgent encapsulates an Ed25519 keypair, agent address, chain ID, and exposes did, didHash, and publicKeyBytes32.
  • Registration helper:
    • registerAgent(signer, agentAddress, ed25519PubKeyBytes32, identityAddress, proof) submits the on-chain registration transaction and returns didHash and txHash.
  • Identity registry:
    • registerAgent(agentAddress, ed25519PubKey, signature) validates the signature, computes didHash, stores the identity, and emits AgentRegistered.
    • computeDidHash(agentAddress) reproduces the same hash on-chain.
    • registrationDigest(agentAddress, operator, ed25519PubKey) defines the signed message structure.
    • verifyRegistration(...) checks the signature against the agent address.

Section sources

Architecture Overview

The registration flow binds three identities together:

  • Agent Ethereum address: proves control via EOA or ERC-1271 contract signature.
  • Ed25519 public key: stored on-chain for off-chain challenge-response authentication.
  • Operator: the caller who controls stake and operational actions.
sequenceDiagram
participant Client as "Client App"
participant AgentObj as "SigvaraAgent"
participant Verifier as "registerAgent()"
participant Contract as "SigvaraIdentity.registerAgent()"
participant Chain as "Blockchain State"
Client->>AgentObj : Create agent (Ed25519 keypair + agentAddress + chainId)
AgentObj-->>Client : did, didHash, publicKeyBytes32
Client->>Verifier : Prepare registration parameters
Verifier->>Contract : registrationDigest(agentAddress, operator, ed25519PubKey)
Verifier->>Verifier : Sign digest with agentSigner
Verifier->>Contract : registerAgent(agentAddress, ed25519PubKey, signature)
Contract->>Contract : verifyRegistration(...)
Contract->>Contract : computeDidHash(agentAddress)
Contract->>Chain : Store AgentIdentity(status=PendingBond)
Contract-->>Verifier : Emit AgentRegistered(didHash)
Verifier-->>Client : { didHash, txHash }
Loading

Diagram sources

Detailed Component Analysis

DID Generation: formatDid() and computeDidHash()

  • formatDid(agentAddress, chainId) constructs the DID string did:sigvara:<chainId>:<agentAddress>.
  • computeDidHash(agentAddress, chainId) hashes the same components using keccak256, mirroring the on-chain function.
  • On-chain, computeDidHash(agentAddress) uses block.chainid and the agent address to produce the same hash deterministically.
flowchart TD
Start(["Start"]) --> Input["Inputs: agentAddress, chainId"]
Input --> Format["formatDid(): build 'did:sigvara:<chainId>:<address>'"]
Input --> Hash["computeDidHash(): keccak256('did:sigvara:', chainId, ':', agentAddress)"]
Format --> OutputDid["Output: DID string"]
Hash --> OutputHash["Output: bytes32 didHash"]
OutputDid --> End(["End"])
OutputHash --> End
Loading

Diagram sources

Section sources

Agent Initialization: SigvaraAgent

  • SigvaraAgent.generate(options) creates a random Ed25519 seed and wraps it into an agent instance.
  • Exposes:
    • agentAddress: the Ethereum address controlling the agent.
    • did: formatted DID string.
    • didHash: deterministic hash of the DID.
    • publicKeyBytes32: raw 32-byte Ed25519 public key for on-chain storage.
classDiagram
class SigvaraAgent {
-keyPair
-_agentAddress
-_chainId
-_challengeTtl
+generate(options)
+agentAddress
+did
+didHash
+publicKeyBytes32
+issueChallenge(peerDid, ttlSeconds)
+signChallengeFor(payload, audience)
}
Loading

Diagram sources

Section sources

Signature Generation for Registration

  • The contract exposes registrationDigest(agentAddress, operator, ed25519PubKey), which encodes:
    • Domain separator including the struct name.
    • block.chainid.
    • Registry address.
    • agentAddress.
    • operator.
    • ed25519PubKey.
  • The client must sign this digest with the agent’s Ethereum private key (EOA or ERC-1271).
  • The SDK helper registerAgent() retrieves the digest from the contract and signs it using the provided agentSigner. If the caller already has a precomputed signature, they can pass it directly.
sequenceDiagram
participant Client as "Client"
participant Verifier as "registerAgent()"
participant Contract as "SigvaraIdentity"
participant Signer as "Agent Signer"
Client->>Verifier : Call registerAgent(signer, agentAddress, ed25519PubKey, identityAddress, proof)
Verifier->>Contract : registrationDigest(agentAddress, operator, ed25519PubKey)
Contract-->>Verifier : Return UNPREFIXED digest
Verifier->>Signer : signMessage(raw digest bytes)
Signer-->>Verifier : Return signature
Verifier->>Contract : registerAgent(agentAddress, ed25519PubKey, signature)
Loading

Diagram sources

Section sources

On-chain Registration: registerAgent()

  • Reverts with InflowsPausedUntil while a guardian pause is running.
  • Validates inputs: non-zero agent address and non-zero public key.
  • Verifies the signature using verifyRegistration(), which applies EIP-191 prefixing and supports EOA and ERC-1271 contracts.
  • Computes didHash via computeDidHash(agentAddress).
  • Ensures the DID has not been registered before.
  • Stores AgentIdentity with status PendingBond, sets the caller as operator, and emits AgentRegistered.
flowchart TD
Start(["registerAgent(agentAddress, ed25519PubKey, signature)"]) --> Validate["Validate inputs<br/>non-zero agentAddress and pubKey"]
Validate --> Verify["verifyRegistration(agentAddress, operator, ed25519PubKey, signature)"]
Verify --> Valid{"Signature valid?"}
Valid --> |No| RevertSig["Revert BadRegistrationSignature"]
Valid --> |Yes| Compute["computeDidHash(agentAddress)"]
Compute --> CheckDup{"Already registered?"}
CheckDup --> |Yes| RevertDup["Revert AlreadyRegistered"]
CheckDup --> |No| Store["Store AgentIdentity(status=PendingBond)<br/>set operator=msg.sender"]
Store --> Emit["Emit AgentRegistered(didHash, operator, agentAddress, ed25519PubKey)"]
Emit --> End(["Return didHash"])
Loading

Diagram sources

Section sources

Step-by-step Registration Examples

Below are step-by-step flows without embedding code content. Use the referenced files for exact implementations.

  1. Agent Creation

    • Generate an Ed25519 keypair and wrap it in SigvaraAgent.
    • Provide agentAddress and chainId.
    • Retrieve publicKeyBytes32, did, and didHash.

    References:

  2. DID Computation

    • Compute did via formatDid(agentAddress, chainId).
    • Compute didHash via computeDidHash(agentAddress, chainId).

    References:

  3. Signature Generation

    • Obtain registrationDigest(agentAddress, operator, ed25519PubKey) from the contract.
    • Sign the returned digest with the agent’s Ethereum private key.
    • Alternatively, provide a precomputed signature if signing externally.

    References:

  4. Transaction Submission

    • Call registerAgent(agentAddress, ed25519PubKey, signature) from the operator wallet.
    • Wait for the transaction receipt and parse the AgentRegistered event to obtain didHash.

    References:

Relationship Between Agent Addresses, DIDs, and On-chain Identity Records

  • Agent address forms part of the DID and determines the didHash.
  • didHash is the primary key for storing AgentIdentity on-chain.
  • AgentIdentity includes:
    • operator: the controller of stake and operations.
    • agentAddress: the claimed agent address.
    • ed25519PubKey: the cryptographic key used for off-chain authentication.
    • status: starts as PendingBond; becomes Active after sufficient stake.
    • registeredAt: timestamp of registration.
erDiagram
AGENT_ADDRESS {
address value PK
}
DID_HASH {
bytes32 value PK
}
AGENT_IDENTITY {
bytes32 didHash PK
address operator
address agentAddress
bytes32 ed25519PubKey
enum status
uint256 registeredAt
}
AGENT_ADDRESS ||--o{ DID_HASH : "forms"
DID_HASH ||--o{ AGENT_IDENTITY : "indexes"
Loading

Diagram sources

Section sources

Dependency Analysis

The registration process depends on:

  • DID utilities for deterministic identity representation.
  • Agent abstraction for key management and metadata.
  • Verifier helper for digest retrieval, signing, and transaction submission.
  • Identity registry for validation, hashing, and state persistence.
graph LR
did["did.ts"] --> agent["agent.ts"]
agent --> verifier["verifier.ts"]
verifier --> identity["SigvaraIdentity.sol"]
Loading

Diagram sources

Section sources

Performance Considerations

  • Gas optimization strategies:
    • Precompute signatures off-chain when possible; pass them directly to registerAgent() to avoid extra calls.
    • Batch operations where feasible (e.g., multiple registrations in separate transactions but prepared offline).
    • Avoid unnecessary reads by caching registrationDigest only when needed; prefer direct signature passing for HSM or Safe scenarios.
  • View functions like computeDidHash and registrationDigest are cheap and help clients validate data before submitting transactions.
  • Event parsing occurs post-transaction; ensure efficient log filtering to minimize overhead.

Troubleshooting Guide

Common registration failures and resolutions:

  • Invalid signature:
    • Cause: Wrong digest, wrong signer, or double-prefixing issues.
    • Resolution: Ensure the digest is retrieved from the contract and signed with the agent’s Ethereum private key. Do not pass hex strings to signMessage; use raw bytes.
    • References:
  • Duplicate registration:
    • Cause: Attempting to register an existing didHash.
    • Resolution: Check getIdentity(didHash) before registering; reuse existing DID if present.
    • References:
  • Zero inputs:
    • Cause: Passing zero address or zero public key.
    • Resolution: Validate inputs before calling registerAgent().
    • References:
  • Registration paused:
    • Cause: A guardian pause is running and registerAgent() reverts with InflowsPausedUntil. A pause ends by itself within three days.
    • Resolution: Retry after the time in the error, or once the guardian or admin lifts the pause.
  • Stake requirements:

Section sources

Conclusion

The Sigvara agent registration workflow ensures trustless, deterministic identity anchoring by binding an Ethereum address, an Ed25519 public key, and a controlled operator. Clients generate a DID and its hash, sign a contract-defined registration digest with the agent’s Ethereum key, and submit a transaction that stores the identity as PendingBond. Subsequent staking transitions the agent to Active. Proper signature handling, input validation, and stake management are essential for successful registration and long-term operation.

Overview

Clone this wiki locally