Skip to content

Agent Registration and Lifecycle

RunTimeAdmin edited this page Nov 2, 2026 · 13 revisions

Agent Registration & Lifecycle

**Referenced Files in This Document** - [SigvaraIdentity.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/SigvaraIdentity.sol) - [SigvaraStaking.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/SigvaraStaking.sol) - [agent.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/agent.ts) - [types.ts](https://github.com/RunTimeAdmin/Sigvara/blob/main/packages/sdk/src/types.ts) - [RegistrationHelper.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/test/helpers/RegistrationHelper.sol) - [SigvaraIdentity.t.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/test/SigvaraIdentity.t.sol) - [InflowGuard.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/InflowGuard.sol) - [TimelockedGrants.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/TimelockedGrants.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 agent registration and lifecycle management in SigvaraIdentity. It covers how agents are registered, what data is stored, how status transitions work, how staking bonds gate activation, and which events are emitted for off-chain tracking. It also provides practical workflow examples using the SDK and Solidity tests as references.

Project Structure

The relevant code lives in:

  • Solidity contracts: src/SigvaraIdentity.sol (identity registry), src/SigvaraStaking.sol (bonding and slashing).
  • TypeScript SDK: packages/sdk/src/agent.ts, packages/sdk/src/types.ts.
  • Test helpers and unit tests: test/helpers/RegistrationHelper.sol, test/SigvaraIdentity.t.sol.
graph TB
subgraph "Solidity"
A["SigvaraIdentity.sol"]
B["SigvaraStaking.sol"]
end
subgraph "TypeScript SDK"
C["agent.ts"]
D["types.ts"]
end
subgraph "Tests"
E["RegistrationHelper.sol"]
F["SigvaraIdentity.t.sol"]
end
C --> A
D --> A
B --> A
E --> A
F --> A
Loading

Diagram sources

Section sources

Core Components

  • SigvaraIdentity: Anchors AI agent identities on-chain, validates registration signatures, manages operator control, public key rotation, status transitions, and DID derivation.
  • SigvaraStaking: Manages stake tokens, minimum stake checks, deposit/withdrawal flows, slashing lifecycle, and coordinates with SigvaraIdentity to update statuses.
  • TypeScript SDK: Provides Ed25519 key generation, DID computation, challenge issuance/signing, and helper types mirroring on-chain enums and structs.

Key responsibilities:

  • Registration requires a signature from the agent address binding chain ID, registry address, agent address, operator, and Ed25519 public key.
  • Agents start in PendingBond; they become Active only after their bond meets or exceeds minimumStake.
  • Status transitions are gated by collateral checks and role permissions. Slashed is terminal.

Section sources

Architecture Overview

Agent registration and lifecycle involve two contracts working together:

  • SigvaraIdentity stores identity metadata and enforces permissioned operations.
  • SigvaraStaking holds bonds and triggers status changes through SigvaraIdentity when deposits reach minimumStake or during slashing.
sequenceDiagram
participant Client as "Client / Operator"
participant SDK as "SDK (agent.ts)"
participant Identity as "SigvaraIdentity"
participant Staking as "SigvaraStaking"
participant StakeView as "IStakeView"
Client->>SDK : Generate Ed25519 keys<br/>Compute didHash
SDK-->>Client : publicKeyBytes32, didHash
Client->>Identity : registerAgent(agentAddress, ed25519PubKey, signature)
Identity->>Identity : verifyRegistration()
Identity->>Identity : computeDidHash()
Identity-->>Client : didHash (status = PendingBond)
Client->>Staking : depositStake(didHash, amount)
Staking->>Identity : getIdentity(didHash)
alt amount >= minimumStake
Staking->>Identity : updateStatus(didHash, Active)
Identity->>StakeView : hasMinimumStake(didHash)
StakeView-->>Identity : true
Identity-->>Staking : success
else amount < minimumStake
Staking-->>Client : success (still PendingBond)
end
Loading

Diagram sources

Detailed Component Analysis

AgentIdentity Struct and Enumerations

  • Fields:
    • operator: Ethereum wallet controlling stake and operations.
    • agentAddress: The agent’s Ethereum address used to derive the DID.
    • ed25519PubKey: Raw 32-byte Ed25519 public key for off-chain authentication.
    • status: One of Active, Suspended, Slashed, PendingBond.
    • registeredAt: Timestamp when the agent was first registered.
  • Enum values:
    • Active: Fully operational and slashable.
    • Suspended: Temporarily halted; may be self-suspended or slashed-initiated.
    • Slashed: Terminal state; no further updates allowed.
    • PendingBond: Registered but not yet bonded to at least minimumStake.
classDiagram
class AgentIdentity {
+address operator
+address agentAddress
+bytes32 ed25519PubKey
+AgentStatus status
+uint256 registeredAt
}
class AgentStatus {
<<enumeration>>
Active
Suspended
Slashed
PendingBond
}
AgentIdentity --> AgentStatus : "uses"
Loading

Diagram sources

Section sources

Registration Process: registerAgent

  • Parameters:
    • agentAddress: The agent’s Ethereum address. Must not be zero.
    • ed25519PubKey: Raw 32-byte Ed25519 public key. Must not be zero.
    • signature: Signature over registrationDigest() produced by agentAddress. Supports EOA and ERC-1271 contracts.
  • Validation:
    • Reverts with InflowsPausedUntil while a guardian pause is running.
    • Zero-address and zero-key checks.
    • Signature verification via SignatureChecker against MessageHashUtils-prefixed digest.
    • Digest binds chainId, registry address, agentAddress, operator, and ed25519PubKey to prevent replay and interception.
  • State setup:
    • Computes deterministic didHash from chainId and agentAddress.
    • Creates AgentIdentity with operator set to caller, status PendingBond, and registeredAt set to current block timestamp.
    • Updates operator index and emits AgentRegistered event.
flowchart TD
Start(["registerAgent entry"]) --> ValidateAgent["Validate agentAddress != 0"]
ValidateAgent --> ValidateKey["Validate ed25519PubKey != 0"]
ValidateKey --> VerifySig["verifyRegistration(agentAddress, operator, pubKey, sig)"]
VerifySig --> |Invalid| RevertSig["Revert BadRegistrationSignature"]
VerifySig --> ComputeDID["computeDidHash(agentAddress)"]
ComputeDID --> CheckDup{"Already registered?"}
CheckDup --> |Yes| RevertDup["Revert AlreadyRegistered"]
CheckDup --> Create["Create AgentIdentity<br/>status=PendingBond<br/>registeredAt=block.timestamp"]
Create --> Index["Update operatorAgents list"]
Index --> Emit["Emit AgentRegistered"]
Emit --> End(["Return didHash"])
Loading

Diagram sources

Section sources

Practical Registration Workflow Examples

  • Using the SDK:
    • Generate an Ed25519 keypair and obtain publicKeyBytes32 and didHash.
    • Sign registrationDigest() with the agent’s private key.
    • Call registerAgent(agentAddress, publicKeyBytes32, signature) from the operator.
    • Deposit stake via SigvaraStaking.depositStake(didHash, amount); if amount >= minimumStake, the agent becomes Active automatically.

References:

Note: Do not copy code here; use the referenced lines as implementation guidance.

Section sources

Relationship Between Registration and Staking

  • Minimum stake requirement:
    • An agent becomes Active only when its stake amount >= minimumStake.
    • If depositStake brings the stake to at least minimumStake while the agent is PendingBond, SigvaraStaking calls updateStatus to Active.
  • Collateral checks:
    • Returning to Active always requires hasMinimumStake(didHash) to return true.
    • If the stake view is unset, returning to Active reverts with StakeViewNotSet.
  • Suspension and exit:
    • Operators can suspend without collateral.
    • Withdrawing stake below minimumStake while Active is disallowed unless fully exiting via suspension first.
  • Guardian pause:
    • depositStake reverts with InflowsPausedUntil while a pause runs, as registerAgent does. Withdrawals, claims, disputes and slash proceedings are never paused.
flowchart TD
Deposit["depositStake(didHash, amount)"] --> CheckStatus{"Agent status == PendingBond?"}
CheckStatus --> |Yes| CheckMin{"amount >= minimumStake?"}
CheckMin --> |Yes| Activate["updateStatus(didHash, Active)"]
CheckMin --> |No| KeepPending["Keep PendingBond"]
CheckStatus --> |No| Credit["Credit stake and emit event"]
Activate --> Done(["Success"])
KeepPending --> Done
Credit --> Done
Loading

Diagram sources

Section sources

State Machine Transitions

Allowed transitions and constraints:

  • PendingBond → Active:
    • Requires depositStake to bring stake >= minimumStake, or operator calling updateStatus(Active) with sufficient collateral.
    • Requires stakeView.hasMinimumStake(didHash) to return true.
  • Active → Suspended:
    • Allowed by operator or staking core. No collateral check required.
  • Suspended → Active:
    • Allowed by operator or staking core. Requires sufficient collateral.
    • If staking core applied a slash suspension lock, operator cannot lift it; only staking core can clear the lock or reinstate.
    • When a slash proposal is dropped (committee cancel, admin veto or ruling, expireDispute, expireSlash), the staking core reinstates the agent only if its bond is still at least minimumStake. Otherwise the agent stays Suspended with the lock cleared, and its operator re-bonds and reactivates it.
  • Any → Slashed:
    • Only STAKING_CORE_ROLE can set Slashed.
    • Slashed is terminal: no further status updates, public key rotation, or transfers allowed.
stateDiagram-v2
[*] --> PendingBond
PendingBond --> Active : "stake >= minimumStake"
Active --> Suspended : "operator or staking core"
Suspended --> Active : "sufficient collateral"
Active --> Slashed : "STAKING_CORE_ROLE"
Suspended --> Slashed : "STAKING_CORE_ROLE"
PendingBond --> Slashed : "STAKING_CORE_ROLE"
Slashed --> [*]
Loading

Diagram sources

Section sources

Events Emitted During Registration and Lifecycle Changes

  • AgentRegistered: Emitted upon successful registration with didHash, operator, agentAddress, and ed25519PubKey.
  • StakeViewSet: Emitted when the stake view oracle is configured.
  • OperatorTransferOffered / OperatorTransferred / OperatorTransferCancelled: Emitted during operator transfer workflows.
  • PublicKeyRotated: Emitted when the Ed25519 public key is rotated.
  • AgentStatusUpdated: Emitted whenever status changes.

These events enable off-chain systems to track registrations, ownership changes, key rotations, and status transitions.

Section sources

Dependency Analysis

  • SigvaraIdentity depends on:
    • OpenZeppelin AccessControlUpgradeable and UUPSUpgradeable for roles and upgradeability.
    • TimelockedGrants and InflowGuard on main: admin revocation of upgrader-administered roles, and the guardian’s pause on registrations.
    • SignatureChecker and MessageHashUtils for signature verification.
    • IStakeView interface to check collateral without importing SigvaraStaking directly. It is wired after deployment, and on main setStakeView needs UPGRADER_ROLE.
  • SigvaraStaking depends on:
    • SigvaraIdentity for identity queries and status updates.
    • IReputationZero interface to zero reputation upon slashing.
    • SafeERC20 for token interactions.
graph LR
Identity["SigvaraIdentity.sol"] --> OZ1["AccessControlUpgradeable"]
Identity --> OZ2["UUPSUpgradeable"]
Identity --> OZ3["SignatureChecker"]
Identity --> OZ4["MessageHashUtils"]
Identity --> StakeView["IStakeView"]
Staking["SigvaraStaking.sol"] --> Identity
Staking --> Rep["IReputationZero"]
Staking --> Token["IERC20 / SafeERC20"]
Loading

Diagram sources

Section sources

Performance Considerations

  • Operator indexing:
    • New storage includes a position map to avoid linear scans during operator transfers.
    • Removal uses swap-and-pop with recorded positions; fallback scan exists for legacy entries.
  • Paged queries:
    • getOperatorAgentsPaged allows safe pagination for large operator lists.
  • Gas costs:
    • Registration is gas-only and does not mint Active status, preventing free bulk registration.
    • Activation requires collateral checks via stake view, adding minimal overhead.

Section sources

Troubleshooting Guide

Common errors and resolutions:

  • BadRegistrationSignature:
    • Ensure the signature is produced by agentAddress over registrationDigest().
    • Verify the operator bound in the digest matches the caller.
  • AlreadyRegistered:
    • Each didHash can only be registered once; do not attempt re-registration.
  • InsufficientCollateral:
    • Before updating to Active, ensure stake >= minimumStake via depositStake.
  • SlashSuspensionLocked:
    • If staking core initiated a slash, operators cannot lift suspension. The lock clears when the proposal is dropped: a committee cancel, an admin veto or ruling, expireDispute 14 days after a dispute, or expireSlash 14 days after the challenge deadline of an undisputed proposal. An executed slash leaves the agent Slashed, which is terminal. clearSlashSuspension is called by the staking contract itself, when a dropped proposal leaves an under-bonded agent Suspended.
  • NotOperator:
    • Only the recorded operator can perform privileged actions like rotatePublicKey or offerOperatorTransfer.

Operational tips:

  • Use SDK.generate to create Ed25519 keys and compute didHash deterministically.
  • Always sign registrationDigest() with the agent’s private key before calling registerAgent.
  • Monitor events AgentRegistered and AgentStatusUpdated for off-chain state synchronization.

Section sources

Conclusion

SigvaraIdentity provides a secure, deterministic agent registration system that ties identity to an Ethereum address and Ed25519 public key, enforced by cryptographic signatures. Agents begin in PendingBond and require a bond meeting minimumStake to become Active. The lifecycle is governed by strict state transitions and collateral checks, with slashing reserved for authorized roles. Events emitted throughout the process support robust off-chain tracking and integration. The TypeScript SDK simplifies key management and DID computation, while tests demonstrate correct parameter usage and edge-case handling.

Overview

Clone this wiki locally