Skip to content

Latest commit

 

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sigvara

Computed reputation and staked slashing for autonomous AI agents, on top of ERC-8004.

As AI agents become independent economic actors, they need a trust score that means something and accountability that costs something. ERC-8004 gives agents a standard on-chain identity and a raw feedback ledger, but deliberately leaves out the hard parts: computing a trustworthy score from that feedback, and putting slashable stake behind it. Sigvara is that layer. It takes ERC-8004 as the identity and feedback substrate, computes a normalized reputation score with an oracle, and enforces bonds and slashing — the accountability the standard omits. (Ed25519 PKI challenge-response for agent-to-agent auth rides alongside.)

Token: not launched yet. The protocol's bond and fee asset will be SVR, a fixed-supply, ownerless ERC-20 launched through Tolly on Arc mainnet with no team allocation, no treasury allocation and no sale. Until its contract address appears in docs/token.md and on sigvara.xyz, there is no genuine SVR; anything trading under the Sigvara name before then, or at any other address after, did not come from this team. On-chain utility begins when the registries deploy to Arc mainnet after audit.

Where this stands. Sigvara is a computed-reputation and staked-slashing layer for autonomous agents on top of ERC-8004: the standard covers identity and raw feedback; Sigvara computes a normalized score and puts slashable bond behind it. Contracts, oracle and SDK are built and tested (Foundry unit, fuzz and invariant tests, Slither in CI). The same protocol ran for about a month on Robinhood Chain testnet with a live hourly oracle under its prior name (see docs/lineage.md). It is now deployed and running on Arc testnet (chain ID 5042002) with a live oracle, payment-verified attestations and a bonded oracle operator — addresses under Deployed contracts and details in docs/arc.md. A second bonded operator runs in checker mode alongside the primary as of 20 September 2026, though both are operated by the same party and only the primary writes scores. The divergence this produced on 20 September was left live and triaged in public rather than seeded away, because it was a delivery failure and not a disagreement; ADR 0003 decides the fix and the pull scanner that implements it is built and off by default, so a payment still enters a score by being reported rather than by having happened. There is no external audit. This is early protocol work with real engineering and a documented testnet lineage, not a mainnet product claim. The two-way CounterAudit integration is live: sealed audit packets carry the agent's Arc identity and score frozen at the moment of the action, and audited outcomes are attested back to the oracle.

Who builds this: docs/WHO.md — builder background, related projects, and contact.

This repo vs. the Countersig hosted platform

This repository (sigvara) is the decentralized protocol: computed reputation and staked slashing on top of ERC-8004 identity, with no central authority. Trust here is enforced by cryptography and cryptoeconomics — nothing to sign up for, nothing to trust us on.

There is a separate product, the Countersig platform (countersig.com, public docs: Countersig-Public), which ships its own npm packages — @countersig/sdk, @countersig/verify, @countersig/mcp, @countersig/react. That platform is a centralized, hosted non-human-identity verification service. It is a different product with a different trust model, built by the same team, but it is not this protocol and does not read from or write to the contracts below. It kept the Countersig name; the protocol did not (see docs/lineage.md for why).

If you're looking for a hosted MCP server or React trust-badge components, those live in the platform repo, not here — though this repo does ship a small MCP tool gated on protocol reputation, built on SigvaraGate, which refuses a tool call to an agent that cannot prove itself or is below a threshold. If you're integrating with the on-chain protocol — DIDs, staked reputation, permissionless verification — you're in the right place, and @sigvara/protocol-sdk (source in packages/sdk, successor to the now-deprecated @countersig/protocol-sdk) is the only SDK for it.

Documentation

Guide Audience
White paper Everyone — the single document: design, trust model, known weaknesses, token, and commands to check every claim against the chain
Ecosystem Overview Everyone — start here to understand the full picture
Quickstart Developers — register your first agent in 10 minutes
Arc Developers — deploy / test on Arc (5042002 / 5042), USDC gas
Deploy to Arc testnet Developers — the first-deployment runbook, step by step
SVR token Everyone — the bond and fee token: contract, distribution, treasury policy, when utility starts
Brand Designers / frontend — colors, type, layout and component rules for every Sigvara surface
CounterAudit Integration Enterprise — embed agent identity in your audit trail
AI Framework Integration Developers — LangChain, AutoGen, CrewAI, Node.js
Reputation Model Everyone — how the 6-factor score works and grows
Payment-backed attestations Developers — how an attestation is tied to a settled payment, and the decay, diversity and evidence-root rules around it
Lineage Everyone — the Robinhood Chain testnet run this protocol came from (addresses, oracle activity, what changed)
Security & Mainnet Readiness Review Everyone — a point-in-time review dated 17 Sep 2026: conditional no-go for mainnet, with the blockers named

Deployed contracts (Arc testnet)

Proxy addresses are stable; the code behind them is not. Every registry here is a UUPS proxy, so the address below never changes and the implementation does. The implementation each one is currently running is recorded under implementations in deployments/5042002.json, with the previous ones kept in history because that is what a rollback targets and what an incident review reads. Those records are read back off the chain, not written from intent: node scripts/record-implementations.mjs --check fails if the file and the chain disagree.

Chain ID 5042002, RPC https://rpc.testnet.arc.io, USDC as native gas. Canonical source is deployments/5042002.json; the table below is a convenience copy.

Contract Address
SigvaraIdentity 0x7e3aFC532eE5d922ab3cc3FFb510c7C8151477Dd
SigvaraReputation 0x6603C96275e85F724Cdf74666b399365e4cA29ed
SigvaraStaking 0xA69d62B2a6774D21A2c15d5d83b27277eD31d35B
SVRToken (testnet faucet token, not the SVR of docs/token.md) 0x41De2D6D55318e197a00E8f5B496eA2790e23E6c
SigvaraOracleBond 0x3c9c12F27DDCa7048840eE3fbF0CAa1C547D8171

Testnet assets have no economic value. The bond token above is a faucet ERC-20 that exists so the staking path can be exercised; it is unrelated to the mainnet SVR.


Protocol Architecture

Sigvara is the computed-reputation and staked-slashing layer on top of the ERC-8004 identity and feedback registries. See docs/architecture.md for the full signal flow.

graph TB
    subgraph sources["Signal sources"]
        CA["CounterAudit<br/>work-outcome attestations"]
        WD["Watchdog scanners<br/>rug / abuse flags"]
    end

    subgraph erc8004["ERC-8004 · identity + feedback (canonical)"]
        EID["Identity Registry<br/>agent identity"]
        EREP["Reputation Registry<br/>raw feedback"]
    end

    subgraph sigvara["Sigvara · the trust layer"]
        OR(["Reputation Oracle<br/>computes the score"])
        REP["SigvaraReputation<br/>computed-score anchor"]
        ST["SigvaraStaking<br/>bonds + slashing"]
    end

    CONS["SDK · consumers · on-chain readers"]

    CA -->|"success / fail"| OR
    WD -->|"flags"| OR
    EID -->|"identity + age"| OR
    EREP -->|"feedback to externalScore"| OR
    OR -->|"propose / finalize"| REP
    ST -->|"slash · zero score"| REP
    CA -->|"giveFeedback"| EREP
    REP -->|"getTotalScore · meetsThreshold"| CONS
Loading

Contracts

Identity layer: ERC-8004. Sigvara has adopted the ERC-8004 Identity Registry as the canonical agent registry and no longer maintains a competing one. Sigvara is the computed-reputation and staked-slashing layer on top of the standard — the parts ERC-8004 deliberately leaves out. See ADR 0001.

Commerce layer: ERC-8183, watched not used. Arc pairs ERC-8004 identity with ERC-8183, where a job is escrow with a verdict written by a named evaluator. That verdict is the one input this protocol cannot check for itself today: the oracle proves a payment settled and takes "it went well" from whoever submitted the attestation. The oracle can now read an ERC-8183 registry (GET /jobs/:address) and reports scored: false on every response, because the ERC is still a Draft and Arc has no canonical deployment. See docs/arc.md.

Contract Role
SigvaraReputation Computed-score anchor. Stores the oracle's normalized, capped 6-factor score — the layer above ERC-8004's raw feedback. Exposes getTotalScore() and meetsThreshold() for on-chain consumers.
SigvaraStaking Staked accountability. Agent bond management with committee-initiated slashing (7-day challenge window, permissionless execution after timelock). No ERC-8004 equivalent — this is the differentiator. Bond token is set by address at deploy: the faucet SVRToken on testnet, SVR on mainnet — the contracts treat it as a plain IERC20 either way.
SigvaraIdentity The registry in use today. did:sigvara registration with proof of control by the agent address, on-chain Ed25519 PKI, bond-gated status (PendingBond → Active) and two-step operator transfer. ERC-8004 is the long-term identity layer (ADR 0001); until that migration lands this contract is what every deployed component reads and writes. Its non-redundant part (the Ed25519 auth key, bond-gated status, transfer history) becomes an extension keyed to an ERC-8004 agent id.
SigvaraOracleBond Oracle accountability. Performance bonds for oracle operators. When wired into SigvaraReputation.operatorBond, only an admitted, bonded operator can propose a score. Deployed on Arc testnet and wired.

The retained contracts use UUPS upgradeable proxies (OpenZeppelin v5), controlled by a governance timelock on mainnet.


DID Method

Direction of travel. ERC-8004 agent ids are the intended long-term identifier (ADR 0001), and the did:sigvara method below will become a view over one. That migration has not happened yet: every agent registered today, on testnet and through the SDK, gets a did:sigvara and is keyed by its didHash.

Format: did:sigvara:<chainId>:<agentAddress>

Example: did:sigvara:1:0x1234...abcd

The didHash index key is derived trustlessly on-chain at registration:

bytes32 didHash = keccak256(
    abi.encodePacked("did:sigvara:", block.chainid, ":", agentAddress)
);

Any party can reproduce the hash without querying contract state. The on-chain derivation prevents off-chain forgery.

DID Document (resolved off-chain)

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/ed25519-2020/v1"
  ],
  "id": "did:sigvara:1:0x1234abcd",
  "controller": "did:pkh:eip155:1:0xOperatorAddress",
  "verificationMethod": [{
    "id": "did:sigvara:1:0x1234abcd#key-1",
    "type": "Ed25519VerificationKey2020",
    "controller": "did:sigvara:1:0x1234abcd",
    "publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiCEsJ"
  }],
  "authentication": ["did:sigvara:1:0x1234abcd#key-1"],
  "assertionMethod": ["did:sigvara:1:0x1234abcd#key-1"]
}

Agent Status State Machine

stateDiagram-v2
    direction LR
    [*] --> PendingBond : registerAgent()\n(signed by the agent address)

    PendingBond --> Active : depositStake() reaches minimumStake

    Active --> Suspended : operator.updateStatus()\nor StakingCore.initiateSlash()
    Suspended --> Active : operator.updateStatus()\nor resolveDispute(false)\nor cancelSlash() / expireDispute()

    Active --> Slashed : StakingCore.executeSlash()
    Suspended --> Slashed : StakingCore.executeSlash()

    Slashed --> [*] : terminal — no further transitions
Loading

Key invariants:

  • Registration mints PendingBond, never Active. An unbonded agent has nothing to slash, so it is not scoreable either.
  • Any transition into Active requires the agent to hold minimumStake — including the ones the staking core makes when a slash is dropped. There is no path back to Active while under-collateralized.
  • Nothing returns to PendingBond. It describes an identity that has never been bonded; an agent does not become un-bonded, it suspends and exits.
  • Only STAKING_CORE_ROLE can set Slashed
  • Slashed is terminal — no key rotation, no status change
  • Suspended agents may rotate their Ed25519 key (key-compromise recovery path)

Reputation System

Scores are computed off-chain by the oracle network and written to SigvaraReputation. The contract stores and serves; it does not compute.

Factor Max Source Formula Status
Fee Activity 20 Settled payment volume to the agent, verified on chain min(20, decayedVolume / PAYMENT_FEE_UNIT), capped per payer live
Success Rate 15 Outcome reported by whoever paid (e.g. CounterAudit) floor(successful / (total + 5) × 15), on decayed weights live
Tenure 30 Span of verified paid activity, faded by how long since the last of it min(30, floor(log₂(spanDays+1) × 3)) × recency live
External Trust 25 Normalized ERC-8004 feedback (linked agents) mean of recognized tags × 25 live
Community 5 Flags from watchdog feeds (e.g. HoodScan), age-weighted max(0, 5 − flags × 2), flags decay on a 30-day half-life live
Propagation 5 Standing of the counterparties that paid the agent 1 pt per fully-trusted counterparty, pro-rated by its score live
Total 100

All six factors are live. Four things shape what the number actually means:

  • Only bonded agents are scored. A newly registered agent is PendingBond and cannot be proposed a score at all. The bond is what makes it slashable, and scoring something unslashable is scoring nothing.
  • Evidence is payment-backed. With PAYMENT_VERIFICATION=required, an attestation must carry the settlement transaction of a real payment to the agent's own address, and the payer is taken from the transfer log rather than asserted by the caller. Self-payments are refused and any one counterparty's evidence is capped. See payment-backed attestations.
  • Evidence decays. Each payment is weighted 0.5 ^ (age / half-life) from the time it settled, and the success rate divides by total + 5 so a decaying record falls toward zero instead of holding a scale-invariant ratio forever. A farmed score is perishable and has to be renewed.
  • A score is earned before it is spendable. getEarnedScore() returns the raw finalized figure; getTotalScore() returns the matured one, which climbs toward it at a fixed rate per day and is what meetsThreshold() checks. Falls apply immediately. Transferring an agent to a new operator restarts maturity.

externalScore is 0 unless the agent links an ERC-8004 identity it owns. See the Reputation Model for provenance detail and payment-backed attestations for how the inputs are verified and committed to.


Slashing Model

Testnet: SLASHING_COMMITTEE_ROLE is held by a single EOA. Mainnet path: a 3-of-5 multisig, then UMA OptimisticOracleV3 or Kleros (isolated in the initiateSlash / disputeSlash interface, replaceable without storage migration).

No slash has been executed end to end on Arc testnet yet. The lifecycle below is covered by the E2E suite, not by a documented live run.

Slash Lifecycle

sequenceDiagram
    participant V as Victim
    participant CM as Slashing committee
    participant ST as SigvaraStaking
    participant ID as SigvaraIdentity
    participant REP as SigvaraReputation

    V->>CM: report agent + evidence package
    CM->>ST: initiateSlash(didHash, victim, evidenceHash)
    ST->>ID: updateStatus(didHash, Suspended)
    Note over ST: 7-day challenge period begins

    alt Operator disputes within window
        Op->>ST: disputeSlash(didHash)
        Note over ST: Proposal → Disputed. Bond stays frozen,<br/>agent stays Suspended
        CM->>ST: resolveDispute(didHash, uphold)
        Note over ST: Upheld slashes; rejected cancels and reinstates.<br/>Unresolved after 14 days, anyone may expireDispute()
    else Challenge period elapses undisputed
        Anyone->>ST: executeSlash(didHash)
        ST->>ID: updateStatus(didHash, Slashed)
        ST->>REP: zeroReputation(didHash)
        ST-->>0xdead: 50% burned
        ST-->>V: 25% to victim
        ST-->>CM: 25% to reporter
    end
Loading

Slash Distribution

Recipient Share Mechanism
address(0xdead) 50% Deflationary burn
Victim 25% Recourse for the harmed party
Committee reporter 25% Incentivizes accurate reporting

Protocol Flows

Agent Registration

sequenceDiagram
    participant Op as Operator
    participant ST as SigvaraStaking
    participant ID as SigvaraIdentity

    Note over Op: Generate Ed25519 keypair off-chain
    Op->>ID: registrationDigest(agentAddress, operator, ed25519PubKey)
    ID-->>Op: digest
    Note over Op: The AGENT address signs the digest (personal_sign).<br/>EOA or ERC-1271 contract — both verify.
    Op->>ID: registerAgent(agentAddress, ed25519PubKey, signature)
    ID->>ID: didHash = keccak256("did:sigvara:" + chainId + ":" + agentAddress)
    ID->>ID: verify signature over the digest
    ID-->>Op: AgentRegistered(didHash, operator, agentAddress, pubKey)
    Note over ID: Status = PendingBond. Resolvable, but not Active,<br/>not scoreable, not slashable.
    Op->>ST: depositStake(didHash, minimumStake)
    ST->>ID: updateStatus(didHash, Active)
    ST-->>Op: bond recorded, agent Active
Loading

Registration requires a signature from the agent address itself, over a digest bound to the chain id, the registry, the operator and the Ed25519 key. Without it anyone could register an address they do not control and choose the public key that verifiers would check against it. The digest is returned unprefixed: a standard signer applies the EIP-191 prefix itself, and returning a pre-prefixed value would make every wallet double-prefix and produce signatures the contract rejects.

Agent-to-Agent (A2A) Trust Verification

sequenceDiagram
    participant A as Agent A
    participant B as Agent B
    participant ID as SigvaraIdentity
    participant REP as SigvaraReputation

    A->>B: request action
    B->>A: challenge payload\n"SIGVARA-VERIFY:{DID}:{nonce}:{timestamp}"
    A->>A: sign payload with Ed25519 private key
    A->>B: { did, signature }
    B->>ID: getIdentity(didHash)
    ID-->>B: { ed25519PubKey, status: Active }
    B->>B: verify Ed25519 signature against pubKey
    B->>REP: meetsThreshold(didHash, 60)
    REP-->>B: true / false
    alt threshold met and signature valid
        B-->>A: action permitted
    else
        B-->>A: rejected
    end
Loading

Reputation Update Lifecycle (Optimistic Scoring)

Reputation updates go through a challenge window before taking effect, rather than writing atomically. This gives the slashing committee a chance to reject a bad proposal before it goes live, without needing a full multi-oracle consensus system.

sequenceDiagram
    participant UC as User / Counterparty
    participant OR as Oracle Network
    participant REP as SigvaraReputation
    participant CM as Slashing Committee

    UC->>OR: attestation + settlement tx of the payment for the job
    OR->>OR: verify the transfer on chain, take the payer from the log
    Note over OR: epoch aggregation over decayed, per-payer-capped evidence
    OR->>OR: compute 6-factor scores + Merkle root over the evidence used
    OR->>REP: proposeReputation(didHash, ReputationData, evidenceRoot)
    REP->>REP: validate per-factor caps, require a bonded agent
    REP-->>OR: ScoreProposed(didHash, proposedAt)
    Note over REP: Challenge window open (e.g. 1-6 hours)

    alt Committee rejects during the window
        CM->>REP: rejectReputation(didHash)
        REP-->>CM: ScoreRejected(didHash)
        Note over REP: Previous finalized score is untouched
    else Window elapses unchallenged
        UC->>REP: finalizeReputation(didHash) — permissionless
        REP-->>UC: ReputationUpdated(didHash, totalScore)
        Note over REP: Score now live for A2A threshold checks
    end
Loading

Every proposal commits to the evidence behind it. evidenceRoots(didHash) holds a Merkle root over the payments the oracle counted; the oracle serves the leaves from GET /evidence/:didHash, unauthenticated at oracle.sigvara.xyz, and anyone can re-verify each payment against the chain, rebuild the root and compare — locally or through verifyEvidence(didHash, leaf, proof). This makes a quietly dropped or invented payment detectable. It does not prove the oracle counted everything it should have; a payment nobody ever submitted leaves no trace to be missing from.


Key Rotation

If an Ed25519 private key is compromised:

  1. Operator calls updateStatus(didHash, Suspended) immediately — invalidates the DID for authentication within one block.
  2. Operator generates a new Ed25519 keypair off-chain.
  3. Operator calls rotatePublicKey(didHash, newEd25519PubKey).
  4. Operator reinstates: updateStatus(didHash, Active).

Slashed agents cannot rotate. The identity is permanently terminated.


TypeScript SDK

npm install @sigvara/protocol-sdk

Refusing work to an agent without standing

The shortest path from a score to a decision. SigvaraGate issues a challenge, checks the proof before touching the chain, spends the nonce so a captured response cannot be replayed, and refuses with a reason you can act on.

import { SigvaraGate } from '@sigvara/protocol-sdk';

const gate = new SigvaraGate({
  rpcUrl: 'https://rpc.testnet.arc.io',
  addresses,                                // deployments/5042002.json
  threshold: 40,
  audience: 'https://your-service.example', // signed into every challenge
});

const challenge = gate.challenge(agentDid);
// ...the agent signs challenge.payload and returns the signature...

const result = await gate.admit(agentDid, challenge, signature);
if (!result.ok) {
  // 'bad_proof' | 'replayed' | 'not_active' | 'below_threshold'
  return refuse(result.reason);
}

Only below_threshold is about reputation. not_active is a real, proven agent that is unbonded, suspended or slashed, and sending that caller off to earn points would be the wrong instruction. The default nonce store is per process; behind a load balancer pass a NonceStore with an atomic consume(). Requires 1.0.0-alpha.9 or later. A worked example lives in examples/mcp-gated-tool.

Agent-to-Agent authentication

import { SigvaraAgent, SigvaraVerifier } from '@sigvara/protocol-sdk';

// Agent A — the prover
const agentA = new SigvaraAgent({
  privateKey: process.env.AGENT_A_ED25519_SEED,  // 32-byte hex seed
  agentAddress: '0xAgentAAddress',
  chainId: 5042002,  // Arc testnet
});

// Agent B — the verifier (has its own identity + a verifier for on-chain lookups)
const agentB = new SigvaraAgent({
  privateKey: process.env.AGENT_B_ED25519_SEED,
  agentAddress: '0xAgentBAddress',
  chainId: 5042002,
});
const verifier = new SigvaraVerifier({
  rpcUrl: 'https://rpc.testnet.arc.io',
  addresses: {
    identity: '0x...  /* from deployments/5042002.json */',
    reputation: '0x...  /* from deployments/5042002.json */',
    staking: '0x...  /* from deployments/5042002.json */',
  },
  chainId: 5042002,
});

// B issues a challenge to A
const challenge = agentB.issueChallenge(agentA.did);

// A signs and returns its DID + signature
const signature = agentA.signChallenge(challenge.payload);

// B verifies: resolves pubkey from chain, checks signature + reputation
const valid = await verifier.verifySignature(agentA.did, challenge.payload, signature);
const trusted = await verifier.meetsThreshold(agentA.did, 60);

On-chain registration (operator)

import { registerAgent, depositStake } from '@sigvara/protocol-sdk';

const { didHash } = await registerAgent(
  signer,                        // ethers.Signer with operator wallet
  agentA.agentAddress,           // the agent's Ethereum address (the DID is derived from it)
  agentA.publicKeyBytes32,       // bytes32 Ed25519 public key
  IDENTITY_CONTRACT_ADDRESS,
  { agentSigner },               // proves control of agentAddress; or { signature } if signed elsewhere
);

// The agent is PendingBond until this lands. The first deposit that carries it over
// minimumStake is what activates it.
// Sends the ERC-20 approval only if the allowance is short, then deposits.
await depositStake(signer, didHash, 1000n * 10n ** 18n, STAKING_CONTRACT_ADDRESS);

DID Document resolution

const didDoc = await verifier.buildDidDocument(agentA.did);
// Returns W3C-compliant DID Document with Ed25519VerificationKey2020

Setup (contracts)

Requires Foundry.

git clone https://github.com/RunTimeAdmin/sigvara
cd sigvara
forge install
forge build
forge test

Deploy and upgrade scripts live in script/. Deploy.s.sol brings up the registries and wires roles; Upgrade.s.sol moves a single UUPS proxy to a new implementation and is covered by test/Upgrade.t.sol. See docs/arc.md for both.

Running the fuzz suite at higher intensity:

FOUNDRY_PROFILE=ci forge test

Testing

Everything below runs offline, with no network access and no deployed contracts.

Suite Count Command
Contracts — unit, fuzz, E2E, upgrade and 6 invariants 246 forge test
Oracle 207 cd oracle && node --test
SDK 58 cd packages/sdk && npx vitest run

Unit Tests (default)

Test individual contract functions in isolation. These run on Foundry's in-memory EVM and require no external network.

forge test                        # all contract tests
FOUNDRY_PROFILE=ci forge test     # denser fuzz runs (5000 iterations)

Invariant Tests

Drive the staking and identity contracts through randomized call sequences and assert properties that must hold in every reachable state — among them that no agent is ever Active while below minimumStake. That one found a real escape: an operator could queue a withdrawal while Suspended, draw a slash, dispute it, and be reinstated under-collateralized.

forge test --match-contract StakingInvariantTest

E2E Integration Tests

Cover the full agent lifecycle: register → stake → propose/finalize reputation → slash → zero-reputation. These run in-memory with deployed fixture contracts.

forge test --match-contract E2EIntegrationTest -vvv

Key scenarios tested:

  • Full lifecycle from registration to slash
  • Slash dispute and agent reinstatement
  • Reputation challenge (committee rejects bad score)
  • Unbonding period enforcement
  • Slash sweeping unbonding queue (dodge prevention)
  • Multiple epochs of score evolution
  • Multiple independent agents

Access Control Summary

Role Holder (Testnet) Mainnet target Permissions
DEFAULT_ADMIN_ROLE Deployer EOA 0x18CBcE50… Governance timelock Grant/revoke all roles, set maturity rate, wire the stake view and oracle bond
UPGRADER_ROLE Deployer EOA 0x18CBcE50… Governance timelock Authorize UUPS upgrades
STAKING_CORE_ROLE SigvaraStaking 0xA69d62B2… SigvaraStaking Suspend / slash agents, zero reputation, clear slash suspensions
ORACLE_ROLE Primary 0x6352b8FF… + checker 0x989aabd6…, both bonded Multiple independently operated bonded operators Propose reputation scores
SLASHING_COMMITTEE_ROLE EOA 0x045d6c1D… 3-of-5 multisig Initiate slash proposals, reject bad scores
Operator Agent registrant Agent registrant Register, suspend, reinstate, rotate key, transfer the agent

Every privileged role on testnet is currently a single EOA, including the slashing committee — the 3-of-5 multisig is the mainnet target, not the present state. Moving admin and upgrade rights to a timelock or Safe is a named mainnet blocker; see the readiness review.

When SigvaraReputation.operatorBond is set, proposeReputation additionally requires the caller to be an admitted, bonded operator in SigvaraOracleBond — so ORACLE_ROLE alone is not enough to write a score. Finalizing stays permissionless.


Ecosystem & Integrations

Sigvara is designed as an open identity layer. Any system that needs to know which AI agent did what can integrate by querying the contracts or consuming CounterAudit enriched packets.

CounterAudit

CounterAudit is the first integration. It shares an owner with Sigvara, as part of the CounterAegis suite, so it is not an arm's-length third party — see docs/ecosystem.md.

Enrichment is live as of 19 September 2026. It had been broken since the rename two days earlier: the code still parsed did:countersig: and hashed that prefix, so every current DID was rejected and the derived hash would not have matched the registry anyway. It was also pointed at the predecessor deployment on Robinhood Chain. Both are fixed, and a verification packet on api.counteraudit.io carries agent_chain_id: 5042002 with the correct hash and live score.

The reverse path went live the same day. It had posted outcomes without the payment.txHash the oracle requires, so every one was rejected. The fix was not to relax the oracle — the payment is the credential, which is what stops attestations being free to manufacture — but to carry the settlement hash through from the caller, who is a party to the payment and knows it. CounterAudit relays it and the oracle still verifies it against the chain independently, so CounterAudit is not a trusted party in that path.

How it works: an ingest call carrying agent_did makes CounterAudit query the Sigvara contracts at seal time and embed the agent's identity and reputation score inside the AES-GCM seal, covered by an RFC 3161 timestamp — proving what the agent's reputation was at the moment of each action rather than today. An ingest also carrying an outcome (success / failure) is reported to the oracle, feeding audited work outcomes back into Success Rate and Fee Activity. Reading and writing the same reputation is what closes the loop.

// Every action your agent takes gets sealed with identity + reputation
await fetch('https://api.counteraudit.io/v1/audit/ingest', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${CA_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    connector_id: 'my-agent',
    agent_did: 'did:sigvara:5042002:0x...',
    raw_event: { action: 'tool_call', tool: 'web_search', query: '...' },
  }),
});

// The sealed packet contains:
// agent_reputation_score: 47
// agent_identity_status: "Active"
// agent_identity_verified: true
// agent_enriched_at: "2026-06-30T16:33:39Z"

See the CounterAudit Integration Guide for full setup instructions.

HoodScan

HoodScan is a rug-risk scanner for Robinhood Chain tokens. When a scan returns a red (high-risk) verdict, it reports the token's deployer address to the reputation oracle as a community flag. If that deployer operates a registered Sigvara agent, the flag lowers its Community factor — so on-chain misbehavior detected off-chain shows up in the agent's reputation. This is the watchdog half of the signal loop: consumers attest to good work, scanners flag bad actors. The oracle's flag endpoint is chain-agnostic, so any scanner can play the same role on Arc.

On-chain consumers

Any smart contract can gate operations on an agent's reputation:

ISigvaraReputation rep = ISigvaraReputation(REPUTATION_ADDRESS);
require(rep.meetsThreshold(didHash, 60), "insufficient reputation");

Roadmap

Phase Timeline Deliverables
Core Protocol Q3 2026 contracts, reputation oracle and @sigvara/protocol-sdk v1.0 built and tested · CounterAudit attestation + HoodScan flag feeds
Arc Port Q4 2026 Arc testnet deployment (5042002, USDC gas) done · oracle epochs against Arc done · SVR launch on Arc mainnet (docs/token.md)
External Trust Q4 2026 externalScore from ERC-8004 feedback — built and tested, reading the ERC-8004 registries on Base Sepolia; it contributes 0 until an agent is linked to an 8004 id whose NFT is held by the same wallet and which carries feedback in a tag the oracle normalizes. No agent is linked today, so this factor is 0 for everyone · agent-vouching graph (propagationScore) — built and live, computed every epoch; it inherits only a counterparty's external standing, so like externalScore it reads 0 network-wide until ERC-8004 standing exists. Reachable ceiling today is 70/100 · deeper ERC-8004 interop (publish CounterAudit validations to the Validation Registry)
Sybil resistance Q4 2026 payment-backed attestations · half-life decay + shrinkage prior · bond required before scoring · self-payment and per-payer diversity caps · tenure in place of calendar age · maturity time-lock · two-step operator transfer · on-chain evidence roots — all done, see payment-backed attestations
Oracle decentralization Q1 2027 oracle operator bonds deployed and wired done · a second bonded operator with challenge-based disagreement done (checker mode, activeCount 2, separate host and RPC provider) · public challenge watcher done (third host, holds no key) · deterministic scoring off the chain clock · operators run by independent parties, which is the part that still matters
Mainnet Registries Q1 2027 Tier-1 security audit · admin and upgrade roles onto a timelock or Safe · one documented end-to-end testnet slash · registry deployment on Arc mainnet (5042) with bonds and scoring fees in SVR
Cross-Chain Q2 2027 Solana + Base state mirroring via LayerZero

The token-launch contract set (fixed-supply SVR, vesting, public sale) and the earlier tokenomics and oracle-first direction docs live under archive/token-launch/. They are out of the build; SVR will launch through Tolly rather than these contracts.


License

MIT

About

Computed reputation and slashable bonds for autonomous agents on ERC-8004. Arc testnet.

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages