From 66d62a87ad7acc5584413a0695303fc1c7dde0db Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 1 Jan 2026 12:20:06 -0700 Subject: [PATCH 01/28] Add CLAUDE.md for Claude Code guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provides context about the SIP repository structure, document format, types, considerations, and workflow for AI-assisted editing. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..2014482d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,58 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository Overview + +This is the Stacks Improvement Proposals (SIPs) repository - a governance documentation repository for the Stacks blockchain. It contains proposals for changes to the blockchain's design, implementation, operation, and governance. + +**This is NOT a code repository.** It is a collection of markdown documents following a formal proposal and ratification process. + +## Repository Structure + +- `sips/` - Contains all SIP documents organized by number (e.g., `sip-000/`, `sip-001/`) +- `considerations/` - Contains Consideration Advisory Board (CAB) information and meeting minutes +- `sips/SIP_TEMPLATE.md` - Template for new SIP submissions + +## SIP Document Format + +All SIPs are markdown files with required sections in order: +1. **Preamble** - Metadata fields (SIP Number, Title, Author, Consideration, Type, Status, Created, License, Sign-off) +2. **Abstract** - High-level summary (max 5000 words) +3. **Copyright** - License information +4. **Introduction** - Problem description and proposed solution +5. **Specification** - Detailed technical specification +6. **Related Work** - Alternative solutions and bibliography +7. **Backwards Compatibility** - Breaking changes and mitigations +8. **Activation** - Timeline, criteria, and process for activation +9. **Reference Implementation** - Links to implementations + +## SIP Types + +- **Consensus** - Requires all implementations to adopt (hard/soft fork) +- **Standard** - Affects implementations but not consensus +- **Operation** - Concerns node operators and miners +- **Meta** - Changes to the SIP process itself +- **Informational** - Provides information without requiring action + +## SIP Considerations (Review Tracks) + +- **Technical** - Technical expertise review +- **Economic** - Token economics, fundraising, grants +- **Governance** - SIP process and committee structure +- **Ethics** - Behavioral standards for office-holders +- **Diversity** - User growth and outreach + +## SIP Workflow + +Draft → Accepted → Recommended → Activation-In-Progress → Ratified + +SIPs can also be: Rejected, Obsolete, Replaced, or Withdrawn + +## Working with SIPs + +When creating or editing SIPs: +- Follow the template in `sips/SIP_TEMPLATE.md` +- Place SIP files in `sips/sip-XXX/sip-XXX-title.md` +- Supplementary materials go in the same directory as `SIP-XXXX-YYY.ext` +- Use approved licenses: BSD-2-Clause, BSD-3-Clause, CC0-1.0, GNU-All-Permissive, GPL-2.0+, LGPL-2.1+ From 49785ba4f30de709a9ee4972123cffccd1f0dd84 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 1 Jan 2026 15:39:10 -0700 Subject: [PATCH 02/28] Add draft SIP-XXX: Standard Trait Definition for Agent Registries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defines three core traits for AI agent management on Stacks: - Identity Registry: agent registration, ownership, metadata - Reputation Registry: client feedback with SIP-018 signatures - Validation Registry: third-party verification workflow Compatible with ERC-8004 Agent Commerce Protocol for cross-chain agent identity using CAIP-2 multichain identifiers. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Opus 4.5 --- sips/sip-XXX/sip-XXX-agent-registries.md | 639 +++++++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 sips/sip-XXX/sip-XXX-agent-registries.md diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md new file mode 100644 index 00000000..59481f8e --- /dev/null +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -0,0 +1,639 @@ +# Preamble + +SIP Number: XXX + +Title: Standard Trait Definition for Agent Registries + +Author(s): +- AIBTC +- Tony <@tony1908> +- Jason Schrader + +Consideration: Technical + +Type: Standard + +Status: Draft + +Created: 2026-01-01 + +License: CC0-1.0 + +Sign-off: + +Layer: Traits + +Discussions-To: https://github.com/stacksgov/sips + +# Abstract + +This SIP defines a standard set of traits for AI agent registries on the Stacks blockchain. It establishes three core registries: an Identity Registry for agent registration and ownership, a Reputation Registry for client feedback and scoring, and a Validation Registry for third-party verification. These traits enable interoperable agent identity, reputation tracking, and validation across applications built on Stacks, while maintaining compatibility with the ERC-8004 Agent Commerce Protocol for cross-chain agent identity. + +# License and Copyright + +This SIP is made available under the terms of the Creative Commons CC0 1.0 Universal license, available at https://creativecommons.org/publicdomain/zero/1.0/ +This SIP's copyright is held by the Stacks Open Internet Foundation. + +# Introduction + +As AI agents become increasingly prevalent in blockchain ecosystems, there is a need for standardized on-chain identity, reputation, and validation mechanisms. Without common standards, each application must implement its own agent management system, leading to fragmentation and preventing agents from building portable reputations across platforms. + +This SIP addresses three core requirements for agent commerce: + +1. **Identity**: Agents need unique, verifiable identities with associated metadata and URIs pointing to off-chain information. + +2. **Reputation**: Clients interacting with agents need a way to provide feedback and view aggregated reputation scores. This feedback system must support both on-chain approval and off-chain signature-based authorization via SIP-018. + +3. **Validation**: Third-party validators (such as auditors, compliance services, or capability verifiers) need a standardized way to approve or reject agents based on specific criteria. + +The Stacks blockchain's programming language, Clarity, provides built-in primitives for defining traits that allow different smart contracts to interoperate. This SIP defines traits for agent registries that any compliant implementation must follow, enabling wallets, applications, and other contracts to interact with agent registries in a consistent manner. + +This standard is designed to be compatible with ERC-8004, enabling cross-chain agent identity using the CAIP-2 multichain identifier format. + +# Specification + +The agent registry standard consists of three separate traits, each addressing a distinct aspect of agent management. Implementations may deploy these as separate contracts or combine them as appropriate. + +## Identity Registry Trait + +The Identity Registry manages agent registration, ownership, and metadata. Unlike SIP-009 NFTs, this registry uses direct ownership maps rather than NFT traits, as agents are not intended to be freely transferable assets. + +### Trait Functions + +#### Register + +`(register () (response uint uint))` + +Register a new agent with an empty URI. Returns the newly assigned agent ID as an unsigned integer. Agent IDs are assigned sequentially starting from 0. + +This method must be defined with `define-public`. + +#### Register with URI + +`(register-with-uri ((token-uri (string-utf8 512))) (response uint uint))` + +Register a new agent with the specified URI. The URI should point to a JSON metadata file describing the agent. Returns the newly assigned agent ID. + +This method must be defined with `define-public`. + +#### Register Full + +`(register-full ((token-uri (string-utf8 512)) (metadata-entries (list 10 {key: (string-utf8 128), value: (buff 512)}))) (response uint uint))` + +Register a new agent with both a URI and initial metadata entries. The metadata entries allow storing up to 10 key-value pairs directly on-chain. Returns the newly assigned agent ID. + +This method must be defined with `define-public`. + +#### Set Agent URI + +`(set-agent-uri ((agent-id uint) (new-uri (string-utf8 512))) (response bool uint))` + +Update the URI for an existing agent. Only the agent owner or an approved operator may call this function. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u1000 | Caller is not authorized (not owner or approved operator) | +| u1001 | Agent with specified ID does not exist | + +#### Set Metadata + +`(set-metadata ((agent-id uint) (key (string-utf8 128)) (value (buff 512))) (response bool uint))` + +Set or update a metadata key-value pair for an agent. Only the agent owner or an approved operator may call this function. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u1000 | Caller is not authorized | +| u1001 | Agent does not exist | +| u1003 | Metadata set operation failed | + +#### Set Approval for All + +`(set-approval-for-all ((agent-id uint) (operator principal) (approved bool)) (response bool uint))` + +Grant or revoke operator permissions for a specific agent. An approved operator can perform actions on behalf of the agent owner, such as updating URIs and metadata. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u1000 | Caller is not authorized | +| u1001 | Agent does not exist | + +#### Owner Of + +`(owner-of ((agent-id uint)) (response principal uint))` + +Return the owner principal of the specified agent. + +This method should be defined as read-only, i.e. `define-read-only`. + +| error code | reason | +| ---------- | ------ | +| u1001 | Agent does not exist | + +#### Get URI + +`(get-uri ((agent-id uint)) (response (string-utf8 512) uint))` + +Return the URI associated with the specified agent. + +This method should be defined as read-only, i.e. `define-read-only`. + +| error code | reason | +| ---------- | ------ | +| u1001 | Agent does not exist | + +#### Get Metadata + +`(get-metadata ((agent-id uint) (key (string-utf8 128))) (response (optional (buff 512)) uint))` + +Return the value for a specific metadata key, or none if the key is not set. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Is Approved for All + +`(is-approved-for-all ((agent-id uint) (operator principal)) (response bool uint))` + +Check if the specified principal is an approved operator for the agent. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Version + +`(get-version () (response (string-ascii 16) uint))` + +Return the contract version string. + +This method should be defined as read-only, i.e. `define-read-only`. + +### Identity Registry Trait Implementation + +```clarity +(define-trait identity-registry-trait + ( + ;; Register a new agent with empty URI + (register () (response uint uint)) + + ;; Register a new agent with URI + (register-with-uri ((string-utf8 512)) (response uint uint)) + + ;; Register a new agent with URI and metadata + (register-full ((string-utf8 512) (list 10 {key: (string-utf8 128), value: (buff 512)})) (response uint uint)) + + ;; Update agent URI + (set-agent-uri (uint (string-utf8 512)) (response bool uint)) + + ;; Set metadata key-value pair + (set-metadata (uint (string-utf8 128) (buff 512)) (response bool uint)) + + ;; Grant or revoke operator permissions + (set-approval-for-all (uint principal bool) (response bool uint)) + + ;; Get agent owner + (owner-of (uint) (response principal uint)) + + ;; Get agent URI + (get-uri (uint) (response (string-utf8 512) uint)) + + ;; Get metadata value for key + (get-metadata (uint (string-utf8 128)) (response (optional (buff 512)) uint)) + + ;; Check operator approval + (is-approved-for-all (uint principal) (response bool uint)) + + ;; Get contract version + (get-version () (response (string-ascii 16) uint)) + ) +) +``` + +## Reputation Registry Trait + +The Reputation Registry enables clients to provide feedback on agents and allows agents to respond. It supports two authorization methods: on-chain approval and SIP-018 signed structured data for off-chain authorization. + +### Trait Functions + +#### Approve Client + +`(approve-client ((agent-id uint) (client principal) (max-index uint)) (response bool uint))` + +Pre-authorize a client to provide feedback for an agent up to the specified index limit. Only the agent owner or approved operator may call this function. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u3000 | Caller is not authorized | +| u3001 | Agent does not exist | + +#### Give Feedback + +`(give-feedback ((agent-id uint) (score uint) (tags (list 10 (string-utf8 64))) (feedback-uri (string-utf8 512))) (response uint uint))` + +Submit feedback for an agent. Requires prior on-chain approval via `approve-client`. Score must be between 0 and 100. Tags provide categorical labels for the feedback. The feedback-uri points to additional off-chain feedback data. Returns the feedback index. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u3000 | Caller is not authorized | +| u3001 | Agent does not exist | +| u3004 | Score exceeds maximum (100) | +| u3005 | Cannot provide feedback on self | +| u3010 | Feedback URI is empty | + +#### Give Feedback Signed + +`(give-feedback-signed ((agent-id uint) (score uint) (tags (list 10 (string-utf8 64))) (feedback-uri (string-utf8 512)) (signature (buff 65)) (auth-expiry uint)) (response uint uint))` + +Submit feedback using SIP-018 signed authorization instead of on-chain approval. The signature must be valid according to SIP-018 structured data signing, and the auth-expiry block height must not have passed. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u3001 | Agent does not exist | +| u3004 | Score exceeds maximum | +| u3005 | Cannot provide feedback on self | +| u3007 | Signature verification failed | +| u3008 | Authorization has expired | +| u3010 | Feedback URI is empty | + +#### Revoke Feedback + +`(revoke-feedback ((agent-id uint) (index uint)) (response bool uint))` + +Revoke previously submitted feedback. Only the original feedback submitter may revoke their feedback. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u3000 | Caller is not authorized | +| u3002 | Feedback entry not found | +| u3003 | Feedback already revoked | + +#### Append Response + +`(append-response ((agent-id uint) (index uint) (response-uri (string-utf8 512))) (response bool uint))` + +Allow an agent to respond to feedback. Only the agent owner or approved operator may respond. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u3000 | Caller is not authorized | +| u3002 | Feedback entry not found | + +#### Read Feedback + +`(read-feedback ((agent-id uint) (client principal) (index uint)) (response {score: uint, tags: (list 10 (string-utf8 64)), feedback-uri: (string-utf8 512), revoked: bool, block-height: uint} uint))` + +Retrieve a specific feedback entry. + +This method should be defined as read-only, i.e. `define-read-only`. + +| error code | reason | +| ---------- | ------ | +| u3002 | Feedback entry not found | + +#### Get Summary + +`(get-summary ((agent-id uint) (filter-client (optional principal)) (filter-tags (optional (list 10 (string-utf8 64))))) (response {count: uint, average-score: uint, total-score: uint} uint))` + +Calculate aggregate reputation metrics for an agent, optionally filtered by client or tags. Returns the count of feedback entries, average score, and total score. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Read All Feedback + +`(read-all-feedback ((agent-id uint) (offset uint) (limit uint)) (response (list 50 {client: principal, index: uint, score: uint, tags: (list 10 (string-utf8 64)), revoked: bool}) uint))` + +Retrieve paginated feedback entries for an agent. Maximum limit is 50 entries per call. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Responders + +`(get-responders ((agent-id uint) (index uint)) (response (list 10 principal) uint))` + +Get the list of principals who have responded to a specific feedback entry. + +This method should be defined as read-only, i.e. `define-read-only`. + +### Reputation Registry Trait Implementation + +```clarity +(define-trait reputation-registry-trait + ( + ;; Pre-authorize a client to provide feedback + (approve-client (uint principal uint) (response bool uint)) + + ;; Submit feedback with on-chain approval + (give-feedback (uint uint (list 10 (string-utf8 64)) (string-utf8 512)) (response uint uint)) + + ;; Submit feedback with SIP-018 signature + (give-feedback-signed (uint uint (list 10 (string-utf8 64)) (string-utf8 512) (buff 65) uint) (response uint uint)) + + ;; Revoke submitted feedback + (revoke-feedback (uint uint) (response bool uint)) + + ;; Respond to feedback + (append-response (uint uint (string-utf8 512)) (response bool uint)) + + ;; Read single feedback entry + (read-feedback (uint principal uint) (response {score: uint, tags: (list 10 (string-utf8 64)), feedback-uri: (string-utf8 512), revoked: bool, block-height: uint} uint)) + + ;; Get aggregate reputation summary + (get-summary (uint (optional principal) (optional (list 10 (string-utf8 64)))) (response {count: uint, average-score: uint, total-score: uint} uint)) + + ;; Read paginated feedback + (read-all-feedback (uint uint uint) (response (list 50 {client: principal, index: uint, score: uint, tags: (list 10 (string-utf8 64)), revoked: bool}) uint)) + + ;; Get responders to feedback + (get-responders (uint uint) (response (list 10 principal) uint)) + ) +) +``` + +## Validation Registry Trait + +The Validation Registry enables third-party validators to approve or reject agents based on specific criteria such as compliance, capability verification, or audits. + +### Trait Functions + +#### Validation Request + +`(validation-request ((agent-id uint) (validator principal) (request-hash (buff 32))) (response bool uint))` + +Request validation from a specific validator. Only the agent owner or approved operator may initiate a request. The request-hash is a unique identifier for this validation request. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u2000 | Caller is not authorized | +| u2001 | Agent does not exist | +| u2003 | Validation request already exists | +| u2004 | Invalid validator (cannot be self) | + +#### Validation Response + +`(validation-response ((request-hash (buff 32)) (score uint) (response-hash (buff 32)) (tag (string-utf8 64))) (response bool uint))` + +Submit a validation response. Only the designated validator for the request may respond. Score must be between 0 and 100, where 0 indicates rejection and higher scores indicate varying levels of approval. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u2000 | Caller is not the designated validator | +| u2002 | Validation request not found | +| u2005 | Score exceeds maximum (100) | + +#### Get Validation Status + +`(get-validation-status ((request-hash (buff 32))) (response {validator: principal, agent-id: uint, score: uint, response-hash: (buff 32), tag: (string-utf8 64), last-update: uint} uint))` + +Retrieve the status of a validation request. + +This method should be defined as read-only, i.e. `define-read-only`. + +| error code | reason | +| ---------- | ------ | +| u2002 | Validation not found | + +#### Get Summary + +`(get-summary ((agent-id uint) (filter-validators (optional (list 10 principal))) (filter-tags (optional (list 10 (string-utf8 64))))) (response {count: uint, average-score: uint, total-score: uint} uint))` + +Calculate aggregate validation metrics for an agent, optionally filtered by validators or tags. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Agent Validations + +`(get-agent-validations ((agent-id uint)) (response (list 1024 (buff 32)) uint))` + +Get all validation request hashes for an agent. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Validator Requests + +`(get-validator-requests ((validator principal)) (response (list 1024 (buff 32)) uint))` + +Get all validation request hashes assigned to a validator. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Identity Registry + +`(get-identity-registry () (response principal uint))` + +Return the principal of the linked Identity Registry contract. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Version + +`(get-version () (response (string-ascii 16) uint))` + +Return the contract version string. + +This method should be defined as read-only, i.e. `define-read-only`. + +### Validation Registry Trait Implementation + +```clarity +(define-trait validation-registry-trait + ( + ;; Request validation from a validator + (validation-request (uint principal (buff 32)) (response bool uint)) + + ;; Submit validation response + (validation-response ((buff 32) uint (buff 32) (string-utf8 64)) (response bool uint)) + + ;; Get validation status + (get-validation-status ((buff 32)) (response {validator: principal, agent-id: uint, score: uint, response-hash: (buff 32), tag: (string-utf8 64), last-update: uint} uint)) + + ;; Get aggregate validation summary + (get-summary (uint (optional (list 10 principal)) (optional (list 10 (string-utf8 64)))) (response {count: uint, average-score: uint, total-score: uint} uint)) + + ;; Get all validations for an agent + (get-agent-validations (uint) (response (list 1024 (buff 32)) uint)) + + ;; Get all requests for a validator + (get-validator-requests (principal) (response (list 1024 (buff 32)) uint)) + + ;; Get linked identity registry + (get-identity-registry () (response principal uint)) + + ;; Get contract version + (get-version () (response (string-ascii 16) uint)) + ) +) +``` + +## Error Code Summary + +### Identity Registry Errors (u1000-u1999) + +| error code | reason | +| ---------- | ------ | +| u1000 | Not authorized | +| u1001 | Agent not found | +| u1002 | Agent already exists | +| u1003 | Metadata set failed | + +### Validation Registry Errors (u2000-u2999) + +| error code | reason | +| ---------- | ------ | +| u2000 | Not authorized | +| u2001 | Agent not found | +| u2002 | Validation not found | +| u2003 | Validation already exists | +| u2004 | Invalid validator | +| u2005 | Invalid response score | + +### Reputation Registry Errors (u3000-u3999) + +| error code | reason | +| ---------- | ------ | +| u3000 | Not authorized | +| u3001 | Agent not found | +| u3002 | Feedback not found | +| u3003 | Feedback already revoked | +| u3004 | Invalid score (exceeds 100) | +| u3005 | Self-feedback not allowed | +| u3006 | Invalid index | +| u3007 | Signature verification failed | +| u3008 | Authorization expired | +| u3009 | Index limit exceeded | +| u3010 | Empty feedback URI | + +# Multichain Identity + +This standard supports cross-chain agent identity using CAIP-2 (Chain Agnostic Improvement Proposal) compliant identifiers. This enables agents registered on Stacks to be referenced from other chains and vice versa. + +## Identifier Format + +``` +stacks::: +``` + +Where: +- `stacks` is the namespace identifier +- `` is the chain identifier (1 for mainnet, 2147483648 for testnet) +- `` is the registry contract name +- `` is the agent's numeric ID + +## Chain Identifiers + +| Network | Chain ID | Example | +| ------- | -------- | ------- | +| Stacks Mainnet | 1 | `stacks:1:identity-registry:42` | +| Stacks Testnet | 2147483648 | `stacks:2147483648:identity-registry:0` | + +This format aligns with ERC-8004 multichain identifiers, enabling consistent agent identity across Ethereum, Stacks, and other supported chains. + +# Implementing in Wallets and Applications + +Developers building applications that interact with agent registries should follow these guidelines: + +## Validating Registry Contracts + +Before interacting with a registry contract, applications should verify that the contract implements the appropriate trait. This can be done by checking the contract's interface or attempting to call read-only functions. + +## Agent Metadata + +The URI returned by `get-uri` should point to a JSON file with the following recommended schema: + +```json +{ + "name": "Agent Name", + "description": "Description of the agent's purpose and capabilities", + "image": "https://example.com/agent-avatar.png", + "capabilities": ["capability1", "capability2"], + "endpoints": { + "api": "https://api.example.com/agent", + "websocket": "wss://ws.example.com/agent" + }, + "version": "1.0.0" +} +``` + +## SIP-018 Signature Integration + +For `give-feedback-signed`, the structured data domain and message should follow SIP-018 conventions. The message should include: +- Agent ID +- Score +- Tags +- Feedback URI +- Expiry block height + +Wallets implementing feedback functionality should provide clear UI for users to understand what they are signing. + +## Displaying Reputation + +When displaying agent reputation: +- Show both the average score and the number of feedback entries +- Indicate whether feedback entries have been revoked +- Display recent feedback with associated tags for context +- Consider filtering by validation status for quality assurance + +# Related Work + +## ERC-8004 + +This SIP is designed to be compatible with [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004), the Agent Commerce Protocol on Ethereum. Both standards define the same three registries (Identity, Reputation, Validation) with equivalent functionality, enabling cross-chain agent identity. + +Key differences in the Stacks implementation: +- Uses Clarity traits instead of Solidity interfaces +- Leverages SIP-018 for signed structured data instead of EIP-712 +- Uses direct ownership maps instead of ERC-721 for identity (agents are not transferable) +- Follows SIP-019 notification patterns for events + +## Other Agent Standards + +Various blockchain projects have proposed agent identity solutions, but most focus solely on identity without integrated reputation and validation. This standard's three-registry approach provides a more complete foundation for agent commerce. + +# Backwards Compatibility + +Not applicable. This is a new standard with no prior implementations to maintain compatibility with. + +# Activation + +This SIP will be considered activated when: + +1. The three trait definitions are deployed to mainnet +2. At least one complete implementation of all three registries is deployed to mainnet +3. The implementation passes all test cases from the reference implementation + +The activation criteria should be met no later than Bitcoin block 900000. + +# Reference Implementations + +## Source Code + +The reference implementation is available at: https://github.com/aibtcdev/erc-8004-stacks + +## Testnet Deployments + +The following contracts are deployed on Stacks testnet: + +- Identity Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.identity-registry` +- Reputation Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.reputation-registry` +- Validation Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.validation-registry` + +## Mainnet Deployments + +Mainnet deployment addresses will be added upon activation. From 9b773bc9dfbcb25402f3a485f44adbfbdf339acb Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 1 Jan 2026 15:50:14 -0700 Subject: [PATCH 03/28] Update SIP-XXX format to match SIP-034 and SIP-000 conventions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change Sign-off to Sign-offs (plural) - Update Discussions-To format with PR link placeholder - Rename 'License and Copyright' to 'Copyright' - Add numbered References section with SIP-018, SIP-019, ERC-8004, CAIP-2 - Update inline references to use numbered citation format [1], [2], etc. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Opus 4.5 --- sips/sip-XXX/sip-XXX-agent-registries.md | 38 +++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 59481f8e..f15d41e3 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -19,20 +19,20 @@ Created: 2026-01-01 License: CC0-1.0 -Sign-off: +Sign-offs: Layer: Traits -Discussions-To: https://github.com/stacksgov/sips +Discussions-To: +- SIP pull request: https://github.com/stacksgov/sips/pull/XXX # Abstract This SIP defines a standard set of traits for AI agent registries on the Stacks blockchain. It establishes three core registries: an Identity Registry for agent registration and ownership, a Reputation Registry for client feedback and scoring, and a Validation Registry for third-party verification. These traits enable interoperable agent identity, reputation tracking, and validation across applications built on Stacks, while maintaining compatibility with the ERC-8004 Agent Commerce Protocol for cross-chain agent identity. -# License and Copyright +# Copyright -This SIP is made available under the terms of the Creative Commons CC0 1.0 Universal license, available at https://creativecommons.org/publicdomain/zero/1.0/ -This SIP's copyright is held by the Stacks Open Internet Foundation. +This SIP is made available under the terms of the Creative Commons CC0 1.0 Universal license, available at https://creativecommons.org/publicdomain/zero/1.0/. This SIP's copyright is held by the Stacks Open Internet Foundation. # Introduction @@ -42,7 +42,7 @@ This SIP addresses three core requirements for agent commerce: 1. **Identity**: Agents need unique, verifiable identities with associated metadata and URIs pointing to off-chain information. -2. **Reputation**: Clients interacting with agents need a way to provide feedback and view aggregated reputation scores. This feedback system must support both on-chain approval and off-chain signature-based authorization via SIP-018. +2. **Reputation**: Clients interacting with agents need a way to provide feedback and view aggregated reputation scores. This feedback system must support both on-chain approval and off-chain signature-based authorization via SIP-018 [1]. 3. **Validation**: Third-party validators (such as auditors, compliance services, or capability verifiers) need a standardized way to approve or reject agents based on specific criteria. @@ -215,7 +215,7 @@ This method should be defined as read-only, i.e. `define-read-only`. ## Reputation Registry Trait -The Reputation Registry enables clients to provide feedback on agents and allows agents to respond. It supports two authorization methods: on-chain approval and SIP-018 signed structured data for off-chain authorization. +The Reputation Registry enables clients to provide feedback on agents and allows agents to respond. It supports two authorization methods: on-chain approval and SIP-018 [1] signed structured data for off-chain authorization. ### Trait Functions @@ -252,7 +252,7 @@ This method must be defined with `define-public`. `(give-feedback-signed ((agent-id uint) (score uint) (tags (list 10 (string-utf8 64))) (feedback-uri (string-utf8 512)) (signature (buff 65)) (auth-expiry uint)) (response uint uint))` -Submit feedback using SIP-018 signed authorization instead of on-chain approval. The signature must be valid according to SIP-018 structured data signing, and the auth-expiry block height must not have passed. +Submit feedback using SIP-018 [1] signed authorization instead of on-chain approval. The signature must be valid according to SIP-018 structured data signing, and the auth-expiry block height must not have passed. This method must be defined with `define-public`. @@ -522,7 +522,7 @@ This method should be defined as read-only, i.e. `define-read-only`. # Multichain Identity -This standard supports cross-chain agent identity using CAIP-2 (Chain Agnostic Improvement Proposal) compliant identifiers. This enables agents registered on Stacks to be referenced from other chains and vice versa. +This standard supports cross-chain agent identity using CAIP-2 [4] compliant identifiers. This enables agents registered on Stacks to be referenced from other chains and vice versa. ## Identifier Format @@ -543,7 +543,7 @@ Where: | Stacks Mainnet | 1 | `stacks:1:identity-registry:42` | | Stacks Testnet | 2147483648 | `stacks:2147483648:identity-registry:0` | -This format aligns with ERC-8004 multichain identifiers, enabling consistent agent identity across Ethereum, Stacks, and other supported chains. +This format aligns with ERC-8004 [3] multichain identifiers, enabling consistent agent identity across Ethereum, Stacks, and other supported chains. # Implementing in Wallets and Applications @@ -573,7 +573,7 @@ The URI returned by `get-uri` should point to a JSON file with the following rec ## SIP-018 Signature Integration -For `give-feedback-signed`, the structured data domain and message should follow SIP-018 conventions. The message should include: +For `give-feedback-signed`, the structured data domain and message should follow SIP-018 [1] conventions. The message should include: - Agent ID - Score - Tags @@ -594,13 +594,13 @@ When displaying agent reputation: ## ERC-8004 -This SIP is designed to be compatible with [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004), the Agent Commerce Protocol on Ethereum. Both standards define the same three registries (Identity, Reputation, Validation) with equivalent functionality, enabling cross-chain agent identity. +This SIP is designed to be compatible with ERC-8004 [3], the Agent Commerce Protocol on Ethereum. Both standards define the same three registries (Identity, Reputation, Validation) with equivalent functionality, enabling cross-chain agent identity. Key differences in the Stacks implementation: - Uses Clarity traits instead of Solidity interfaces -- Leverages SIP-018 for signed structured data instead of EIP-712 +- Leverages SIP-018 [1] for signed structured data instead of EIP-712 - Uses direct ownership maps instead of ERC-721 for identity (agents are not transferable) -- Follows SIP-019 notification patterns for events +- Follows SIP-019 [2] notification patterns for events ## Other Agent Standards @@ -637,3 +637,13 @@ The following contracts are deployed on Stacks testnet: ## Mainnet Deployments Mainnet deployment addresses will be added upon activation. + +# References + +[1] SIP-018: Signed Structured Data - https://github.com/stacksgov/sips/blob/main/sips/sip-018/sip-018-signed-structured-data.md + +[2] SIP-019: Notifications for Token Metadata Updates - https://github.com/stacksgov/sips/blob/main/sips/sip-019/sip-019-token-metadata-update-notifications.md + +[3] ERC-8004: Agent Commerce Protocol - https://eips.ethereum.org/EIPS/eip-8004 + +[4] CAIP-2: Blockchain ID Specification - https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md From ea82c1cf2aa59081451c2a480df5dfb3edbd6175 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 1 Jan 2026 16:05:30 -0700 Subject: [PATCH 04/28] Remove block deadline from SIP-XXX activation criteria MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Activation now requires only: - Trait definitions deployed to mainnet - At least one complete implementation deployed - Implementation passes reference test cases 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Opus 4.5 --- sips/sip-XXX/sip-XXX-agent-registries.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index f15d41e3..432d2f4e 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -618,8 +618,6 @@ This SIP will be considered activated when: 2. At least one complete implementation of all three registries is deployed to mainnet 3. The implementation passes all test cases from the reference implementation -The activation criteria should be met no later than Bitcoin block 900000. - # Reference Implementations ## Source Code From c0b74ebcb0d96f4947e4ec528bd560d39a9f61f0 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 5 Feb 2026 22:04:45 -0700 Subject: [PATCH 05/28] feat: add use cases and ERC-8004 Solidity reference Incorporates community feedback from @xyzerobtc on PR #249: - Add use cases and common patterns section covering portable reputation, spam prevention, transparency, dispute resolution, and Bitcoin-level security - Add ERC-8004 Solidity contracts as additional reference implementation Co-Authored-By: Claude Opus 4.6 --- sips/sip-XXX/sip-XXX-agent-registries.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 432d2f4e..9f93bf1d 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -582,6 +582,20 @@ For `give-feedback-signed`, the structured data domain and message should follow Wallets implementing feedback functionality should provide clear UI for users to understand what they are signing. +## Use Cases and Common Patterns + +The following patterns illustrate how agent registries can be used across applications: + +1. **Portable Reputation** - Agents maintain a verifiable track record that can move across platforms and chains. An agent registered on Stacks can reference its reputation history when interacting with services on other chains via CAIP-2 identifiers. + +2. **Spam Prevention** - Index limits and approval mechanisms in the Reputation Registry reduce review bombing and low-quality feedback. The `approve-client` function ensures only authorized clients can submit feedback. + +3. **Transparency** - All feedback is public and immutable unless explicitly revoked by the author. The `revoke-feedback` function allows authors to retract feedback while preserving the on-chain record that it existed. + +4. **Dispute Resolution** - Agents can respond to negative feedback via `append-response` and provide on-chain context, creating a transparent dialogue between agents and their clients. + +5. **Bitcoin-Level Security** - Stacks settlement ensures reputation data remains durable and tamper-resistant, even if individual platforms disappear. + ## Displaying Reputation When displaying agent reputation: @@ -622,7 +636,9 @@ This SIP will be considered activated when: ## Source Code -The reference implementation is available at: https://github.com/aibtcdev/erc-8004-stacks +Reference implementations are available at: +- Stacks (Clarity): https://github.com/aibtcdev/erc-8004-stacks +- Ethereum (Solidity): https://github.com/erc-8004/erc-8004-contracts ## Testnet Deployments From 575c6c7fba2b9c0a8d5d15a99ca03b8512fb0db6 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:18:55 -0700 Subject: [PATCH 06/28] docs(sip-xxx): update Abstract to reflect v2.0.0 features Update Abstract section to accurately describe v2.0.0 implementation: - Identity Registry implements SIP-009 NFT trait (not just ownership maps) - Agent wallet system via reserved metadata keys - Signed value scoring with WAD normalization (int + decimals, not uint) - Three authorization paths: permissionless, on-chain approval, SIP-018 signed - Progressive validation responses (multiple updates per request) - Maintains ERC-8004 cross-chain compatibility Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 9f93bf1d..470b0b7c 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -28,7 +28,7 @@ Discussions-To: # Abstract -This SIP defines a standard set of traits for AI agent registries on the Stacks blockchain. It establishes three core registries: an Identity Registry for agent registration and ownership, a Reputation Registry for client feedback and scoring, and a Validation Registry for third-party verification. These traits enable interoperable agent identity, reputation tracking, and validation across applications built on Stacks, while maintaining compatibility with the ERC-8004 Agent Commerce Protocol for cross-chain agent identity. +This SIP defines a standard set of traits for AI agent registries on the Stacks blockchain. It establishes three core registries: an Identity Registry implementing the SIP-009 NFT trait for agent registration and ownership, a Reputation Registry supporting signed value scoring with WAD normalization and three authorization paths (permissionless, on-chain approval, SIP-018 signed), and a Validation Registry enabling progressive third-party verification responses. The Identity Registry includes an agent wallet system via reserved metadata keys, enabling agents to control their own wallets separate from owner accounts. These traits enable interoperable agent identity, reputation tracking, and validation across applications built on Stacks, while maintaining compatibility with the ERC-8004 Agent Commerce Protocol for cross-chain agent identity. # Copyright @@ -40,11 +40,11 @@ As AI agents become increasingly prevalent in blockchain ecosystems, there is a This SIP addresses three core requirements for agent commerce: -1. **Identity**: Agents need unique, verifiable identities with associated metadata and URIs pointing to off-chain information. +1. **Identity**: Agents need unique, verifiable identities with associated metadata and URIs pointing to off-chain information. By implementing the SIP-009 NFT trait, agent identities gain standard wallet visibility, explorer integration, and transfer event tracking. The registry also supports an agent wallet system through reserved metadata keys, enabling agents to control separate wallets from their owner accounts—agents can prove wallet ownership via transaction signatures (tx-sender) or owners can provide SIP-018 signatures to update the wallet. -2. **Reputation**: Clients interacting with agents need a way to provide feedback and view aggregated reputation scores. This feedback system must support both on-chain approval and off-chain signature-based authorization via SIP-018 [1]. +2. **Reputation**: Clients interacting with agents need a way to provide feedback and view aggregated reputation scores using signed values (integer value + decimals 0-18) that support negative feedback and high-precision ratings. The feedback system operates permissionlessly by default (no pre-approval required), with self-feedback blocked via cross-contract authorization checks. Agents can optionally enable on-chain approval or accept SIP-018 signed feedback, providing three distinct authorization paths to balance openness with control. -3. **Validation**: Third-party validators (such as auditors, compliance services, or capability verifiers) need a standardized way to approve or reject agents based on specific criteria. +3. **Validation**: Third-party validators (such as auditors, compliance services, or capability verifiers) need a standardized way to approve or reject agents based on specific criteria. The validation system supports progressive responses, allowing validators to submit multiple updates per request (e.g., preliminary → final scores) following a soft-to-hard finality workflow without monotonic constraints. The Stacks blockchain's programming language, Clarity, provides built-in primitives for defining traits that allow different smart contracts to interoperate. This SIP defines traits for agent registries that any compliant implementation must follow, enabling wallets, applications, and other contracts to interact with agent registries in a consistent manner. @@ -56,7 +56,7 @@ The agent registry standard consists of three separate traits, each addressing a ## Identity Registry Trait -The Identity Registry manages agent registration, ownership, and metadata. Unlike SIP-009 NFTs, this registry uses direct ownership maps rather than NFT traits, as agents are not intended to be freely transferable assets. +The Identity Registry manages agent registration, ownership, and metadata. It implements the SIP-009 NFT trait, providing standard wallet and explorer compatibility while maintaining sequential agent ID assignment and cross-contract authorization checks. ### Trait Functions From 31b10c90037631b0e3bc1abcf98c97a0863ae516 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:23:55 -0700 Subject: [PATCH 07/28] feat(sip-xxx): rewrite Identity Registry specification for v2.0.0 Complete rewrite of Identity Registry trait definition and function documentation to match v2.0.0 NFT model: - Add SIP-009 NFT trait functions: get-last-token-id, get-token-uri, get-owner, transfer - Add agent wallet functions: set-agent-wallet-direct, set-agent-wallet-signed, unset-agent-wallet, get-agent-wallet - Add is-authorized-or-owner for cross-contract authorization checks - Update all read-only function signatures to match contract (remove response wrappers) - Add new error codes u1004-u1008 for reserved keys, invalid sender, wallet state, and signatures - Document reserved key "agentWallet" restrictions in register-full and set-metadata - Update trait definition to include all v2.0.0 functions All function signatures now exactly match the identity-registry.clar v2.0.0 contract source. Co-Authored-By: Claude Opus 4.6 --- sips/sip-XXX/sip-XXX-agent-registries.md | 178 ++++++++++++++++++++--- 1 file changed, 154 insertions(+), 24 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 470b0b7c..ab8202c3 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -80,10 +80,15 @@ This method must be defined with `define-public`. `(register-full ((token-uri (string-utf8 512)) (metadata-entries (list 10 {key: (string-utf8 128), value: (buff 512)}))) (response uint uint))` -Register a new agent with both a URI and initial metadata entries. The metadata entries allow storing up to 10 key-value pairs directly on-chain. Returns the newly assigned agent ID. +Register a new agent with both a URI and initial metadata entries. The metadata entries allow storing up to 10 key-value pairs directly on-chain. The reserved key "agentWallet" cannot be included in metadata entries; the agent wallet is automatically set to the owner on registration. Returns the newly assigned agent ID. This method must be defined with `define-public`. +| error code | reason | +| ---------- | ------ | +| u1003 | Metadata set operation failed | +| u1004 | Reserved key (agentWallet cannot be set during registration) | + #### Set Agent URI `(set-agent-uri ((agent-id uint) (new-uri (string-utf8 512))) (response bool uint))` @@ -101,7 +106,7 @@ This method must be defined with `define-public`. `(set-metadata ((agent-id uint) (key (string-utf8 128)) (value (buff 512))) (response bool uint))` -Set or update a metadata key-value pair for an agent. Only the agent owner or an approved operator may call this function. +Set or update a metadata key-value pair for an agent. Only the agent owner or an approved operator may call this function. The reserved key "agentWallet" cannot be set via this function; use the agent wallet functions instead. This method must be defined with `define-public`. @@ -110,6 +115,7 @@ This method must be defined with `define-public`. | u1000 | Caller is not authorized | | u1001 | Agent does not exist | | u1003 | Metadata set operation failed | +| u1004 | Reserved key (agentWallet cannot be set directly) | #### Set Approval for All @@ -126,52 +132,148 @@ This method must be defined with `define-public`. #### Owner Of -`(owner-of ((agent-id uint)) (response principal uint))` +`(owner-of ((agent-id uint)) (optional principal))` + +Return the owner principal of the specified agent, or none if the agent does not exist. This is a legacy function; prefer using get-owner for SIP-009 compatibility. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get URI -Return the owner principal of the specified agent. +`(get-uri ((agent-id uint)) (optional (string-utf8 512)))` + +Return the URI associated with the specified agent, or none if not set or agent does not exist. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Metadata + +`(get-metadata ((agent-id uint) (key (string-utf8 128))) (optional (buff 512)))` + +Return the value for a specific metadata key, or none if the key is not set or agent does not exist. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Is Approved for All + +`(is-approved-for-all ((agent-id uint) (operator principal)) bool)` + +Check if the specified principal is an approved operator for the agent. Returns false if agent does not exist. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Version + +`(get-version () (string-utf8 8))` + +Return the contract version string. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Last Token ID + +`(get-last-token-id () (response uint uint))` + +Return the ID of the most recently registered agent. This is a SIP-009 NFT trait function. This method should be defined as read-only, i.e. `define-read-only`. | error code | reason | | ---------- | ------ | -| u1001 | Agent does not exist | +| u1001 | No agents have been registered yet | -#### Get URI +#### Get Token URI -`(get-uri ((agent-id uint)) (response (string-utf8 512) uint))` +`(get-token-uri ((token-id uint)) (response (optional (string-utf8 512)) uint))` -Return the URI associated with the specified agent. +Return the URI for the specified token ID (agent ID). Returns none wrapped in an ok response if the URI is empty or token doesn't exist. This is a SIP-009 NFT trait function. This method should be defined as read-only, i.e. `define-read-only`. +#### Get Owner + +`(get-owner ((token-id uint)) (response (optional principal) uint))` + +Return the owner principal for the specified token ID (agent ID). Returns none wrapped in an ok response if the token doesn't exist. This is a SIP-009 NFT trait function that complements the legacy owner-of function. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Transfer + +`(transfer ((token-id uint) (sender principal) (recipient principal)) (response bool uint))` + +Transfer agent identity NFT from sender to recipient. Only the token owner can initiate transfers, and tx-sender must equal the sender parameter. Clears agent-wallet on transfer to prevent stale wallet associations. This is a SIP-009 NFT trait function. + +This method must be defined with `define-public`. + | error code | reason | | ---------- | ------ | +| u1000 | Caller is not authorized (sender is not the token owner) | | u1001 | Agent does not exist | +| u1005 | Invalid sender (tx-sender must equal sender parameter) | -#### Get Metadata +#### Get Agent Wallet -`(get-metadata ((agent-id uint) (key (string-utf8 128))) (response (optional (buff 512)) uint))` +`(get-agent-wallet ((agent-id uint)) (optional principal))` -Return the value for a specific metadata key, or none if the key is not set. +Return the agent wallet principal for the specified agent, or none if not set. The agent wallet is a reserved metadata key that allows agents to control a separate wallet from their owner account. This method should be defined as read-only, i.e. `define-read-only`. -#### Is Approved for All +#### Set Agent Wallet Direct -`(is-approved-for-all ((agent-id uint) (operator principal)) (response bool uint))` +`(set-agent-wallet-direct ((agent-id uint)) (response bool uint))` -Check if the specified principal is an approved operator for the agent. +Set the agent wallet to tx-sender. Requires caller to be authorized (owner or approved operator). The wallet proves ownership via transaction signature. Returns error if tx-sender is already the current wallet. -This method should be defined as read-only, i.e. `define-read-only`. +This method must be defined with `define-public`. -#### Get Version +| error code | reason | +| ---------- | ------ | +| u1000 | Caller is not authorized | +| u1001 | Agent does not exist | +| u1006 | Wallet already set to this principal | -`(get-version () (response (string-ascii 16) uint))` +#### Set Agent Wallet Signed -Return the contract version string. +`(set-agent-wallet-signed ((agent-id uint) (new-wallet principal) (deadline uint) (signature (buff 65))) (response bool uint))` + +Set agent wallet using SIP-018 signature from the new wallet principal. Requires caller to be authorized (owner or approved operator). The signature must be valid and deadline must be current block height or future (within MAX_DEADLINE_DELAY of 1500 blocks). + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u1000 | Caller is not authorized | +| u1001 | Agent does not exist | +| u1007 | Expired signature (deadline passed or exceeds MAX_DEADLINE_DELAY) | +| u1008 | Invalid signature (recovery failed or doesn't match new-wallet) | + +#### Unset Agent Wallet + +`(unset-agent-wallet ((agent-id uint)) (response bool uint))` + +Remove the agent wallet association. Requires caller to be authorized (owner or approved operator). + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | +| u1000 | Caller is not authorized | +| u1001 | Agent does not exist | + +#### Is Authorized or Owner + +`(is-authorized-or-owner ((spender principal) (agent-id uint)) (response bool uint))` + +Check if the specified principal is either the owner or an approved operator for the agent. Used for cross-contract authorization checks (e.g., reputation registry self-feedback prevention). This method should be defined as read-only, i.e. `define-read-only`. +| error code | reason | +| ---------- | ------ | +| u1001 | Agent does not exist | + ### Identity Registry Trait Implementation ```clarity @@ -195,20 +297,43 @@ This method should be defined as read-only, i.e. `define-read-only`. ;; Grant or revoke operator permissions (set-approval-for-all (uint principal bool) (response bool uint)) - ;; Get agent owner - (owner-of (uint) (response principal uint)) + ;; Set agent wallet to tx-sender + (set-agent-wallet-direct (uint) (response bool uint)) + + ;; Set agent wallet with SIP-018 signature + (set-agent-wallet-signed (uint principal uint (buff 65)) (response bool uint)) + + ;; Remove agent wallet + (unset-agent-wallet (uint) (response bool uint)) + + ;; Transfer agent identity NFT + (transfer (uint principal principal) (response bool uint)) + + ;; Get agent owner (legacy) + (owner-of (uint) (optional principal)) ;; Get agent URI - (get-uri (uint) (response (string-utf8 512) uint)) + (get-uri (uint) (optional (string-utf8 512))) ;; Get metadata value for key - (get-metadata (uint (string-utf8 128)) (response (optional (buff 512)) uint)) + (get-metadata (uint (string-utf8 128)) (optional (buff 512))) ;; Check operator approval - (is-approved-for-all (uint principal) (response bool uint)) + (is-approved-for-all (uint principal) bool) + + ;; Get agent wallet + (get-agent-wallet (uint) (optional principal)) + + ;; Check if spender is authorized or owner + (is-authorized-or-owner (principal uint) (response bool uint)) ;; Get contract version - (get-version () (response (string-ascii 16) uint)) + (get-version () (string-utf8 8)) + + ;; SIP-009 NFT trait functions + (get-last-token-id () (response uint uint)) + (get-token-uri (uint) (response (optional (string-utf8 512)) uint)) + (get-owner (uint) (response (optional principal) uint)) ) ) ``` @@ -492,6 +617,11 @@ This method should be defined as read-only, i.e. `define-read-only`. | u1001 | Agent not found | | u1002 | Agent already exists | | u1003 | Metadata set failed | +| u1004 | Reserved key (agentWallet cannot be set via set-metadata) | +| u1005 | Invalid sender (tx-sender must match sender parameter in transfer) | +| u1006 | Wallet already set (tx-sender is already the agent wallet) | +| u1007 | Expired signature (deadline passed or exceeds MAX_DEADLINE_DELAY) | +| u1008 | Invalid signature (recovery failed or doesn't match expected principal) | ### Validation Registry Errors (u2000-u2999) From 8628ed64a8be9b7ebac67859a73016ed9fc6c134 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:28:45 -0700 Subject: [PATCH 08/28] feat(reputation): rewrite Reputation Registry spec for v2.0.0 Complete rewrite of Reputation Registry trait and documentation to match v2.0.0 contract implementation. Key changes: - Signed values: Replace uint score with int value + uint value-decimals (0-18) - String tags: Replace (list 10 (string-utf8 64)) with tag1/tag2 - Three authorization paths: permissionless, approved, signed (SIP-018) - New parameters: endpoint (emit-only), feedback-hash (buff 32) - WAD normalization: get-summary uses 18-decimal precision with mode-based scaling - New read functions: get-last-index, get-clients, get-approved-limit, get-response-count - Updated error codes: added u3006, u3011, removed stale codes - append-response: now accepts client param, response-hash, anyone can respond All function signatures now exactly match reputation-registry.clar v2.0.0. Co-Authored-By: Claude Opus 4.6 --- sips/sip-XXX/sip-XXX-agent-registries.md | 137 ++++++++++++++++------- 1 file changed, 97 insertions(+), 40 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index ab8202c3..4816241f 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -346,108 +346,150 @@ The Reputation Registry enables clients to provide feedback on agents and allows #### Approve Client -`(approve-client ((agent-id uint) (client principal) (max-index uint)) (response bool uint))` +`(approve-client ((agent-id uint) (client principal) (index-limit uint)) (response bool uint))` -Pre-authorize a client to provide feedback for an agent up to the specified index limit. Only the agent owner or approved operator may call this function. +Pre-authorize a client to provide feedback for an agent up to the specified index limit. Only the agent owner or approved operator may call this function. This enables rate-limited, on-chain approval for trusted clients. This method must be defined with `define-public`. | error code | reason | | ---------- | ------ | | u3000 | Caller is not authorized | + +#### Give Feedback (Permissionless) + +`(give-feedback ((agent-id uint) (value int) (value-decimals uint) (tag1 (string-utf8 64)) (tag2 (string-utf8 64)) (endpoint (string-utf8 512)) (feedback-uri (string-utf8 512)) (feedback-hash (buff 32))) (response uint uint))` + +Submit feedback for an agent without prior authorization. Anyone may submit feedback except the agent owner or approved operators (self-feedback is blocked). The value is a signed integer with decimals 0-18 for high-precision ratings (WAD-normalized in summaries). Tags provide categorical labels. The endpoint is emit-only (for off-chain routing), while feedback-uri and feedback-hash provide content verification. Returns the feedback index. + +This method must be defined with `define-public`. + +| error code | reason | +| ---------- | ------ | | u3001 | Agent does not exist | +| u3005 | Cannot provide feedback on self | +| u3011 | Invalid decimals (exceeds 18) | -#### Give Feedback +#### Give Feedback (Approved) -`(give-feedback ((agent-id uint) (score uint) (tags (list 10 (string-utf8 64))) (feedback-uri (string-utf8 512))) (response uint uint))` +`(give-feedback-approved ((agent-id uint) (value int) (value-decimals uint) (tag1 (string-utf8 64)) (tag2 (string-utf8 64)) (endpoint (string-utf8 512)) (feedback-uri (string-utf8 512)) (feedback-hash (buff 32))) (response uint uint))` -Submit feedback for an agent. Requires prior on-chain approval via `approve-client`. Score must be between 0 and 100. Tags provide categorical labels for the feedback. The feedback-uri points to additional off-chain feedback data. Returns the feedback index. +Submit feedback with on-chain approval via `approve-client`. The client must have been pre-authorized with an index limit greater than or equal to the next feedback index. Self-feedback is blocked. Returns the feedback index. This method must be defined with `define-public`. | error code | reason | | ---------- | ------ | -| u3000 | Caller is not authorized | | u3001 | Agent does not exist | -| u3004 | Score exceeds maximum (100) | | u3005 | Cannot provide feedback on self | -| u3010 | Feedback URI is empty | +| u3009 | Index limit exceeded | +| u3011 | Invalid decimals (exceeds 18) | -#### Give Feedback Signed +#### Give Feedback (Signed) -`(give-feedback-signed ((agent-id uint) (score uint) (tags (list 10 (string-utf8 64))) (feedback-uri (string-utf8 512)) (signature (buff 65)) (auth-expiry uint)) (response uint uint))` +`(give-feedback-signed ((agent-id uint) (value int) (value-decimals uint) (tag1 (string-utf8 64)) (tag2 (string-utf8 64)) (endpoint (string-utf8 512)) (feedback-uri (string-utf8 512)) (feedback-hash (buff 32)) (signer principal) (index-limit uint) (expiry uint) (signature (buff 65))) (response uint uint))` -Submit feedback using SIP-018 [1] signed authorization instead of on-chain approval. The signature must be valid according to SIP-018 structured data signing, and the auth-expiry block height must not have passed. +Submit feedback using SIP-018 signed authorization. The signer must be the agent owner or approved operator, and must sign a structured data message containing agent-id, client (tx-sender), index-limit, and expiry. The signature is verified using secp256k1 recovery. Self-feedback is blocked. Returns the feedback index. This method must be defined with `define-public`. | error code | reason | | ---------- | ------ | +| u3000 | Signer is not authorized | | u3001 | Agent does not exist | -| u3004 | Score exceeds maximum | | u3005 | Cannot provide feedback on self | | u3007 | Signature verification failed | | u3008 | Authorization has expired | -| u3010 | Feedback URI is empty | +| u3009 | Index limit exceeded | +| u3011 | Invalid decimals (exceeds 18) | #### Revoke Feedback `(revoke-feedback ((agent-id uint) (index uint)) (response bool uint))` -Revoke previously submitted feedback. Only the original feedback submitter may revoke their feedback. +Revoke previously submitted feedback. Only the original feedback submitter (tx-sender) may revoke their own feedback. Revoked feedback is excluded from summary aggregations. This method must be defined with `define-public`. | error code | reason | | ---------- | ------ | -| u3000 | Caller is not authorized | | u3002 | Feedback entry not found | | u3003 | Feedback already revoked | +| u3006 | Invalid index (must be > 0) | #### Append Response -`(append-response ((agent-id uint) (index uint) (response-uri (string-utf8 512))) (response bool uint))` +`(append-response ((agent-id uint) (client principal) (index uint) (response-uri (string-utf8 512)) (response-hash (buff 32))) (response bool uint))` -Allow an agent to respond to feedback. Only the agent owner or approved operator may respond. +Allow anyone to respond to feedback. Multiple responders can respond to the same feedback entry, and each responder can respond multiple times (tracked via response-count). The response-uri must not be empty. This method must be defined with `define-public`. | error code | reason | | ---------- | ------ | -| u3000 | Caller is not authorized | | u3002 | Feedback entry not found | +| u3006 | Invalid index (must be > 0) | +| u3010 | Response URI is empty | #### Read Feedback -`(read-feedback ((agent-id uint) (client principal) (index uint)) (response {score: uint, tags: (list 10 (string-utf8 64)), feedback-uri: (string-utf8 512), revoked: bool, block-height: uint} uint))` +`(read-feedback ((agent-id uint) (client principal) (index uint)) (response (optional {value: int, value-decimals: uint, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}) uint))` -Retrieve a specific feedback entry. +Retrieve a specific feedback entry. Returns none if the feedback does not exist. This method should be defined as read-only, i.e. `define-read-only`. -| error code | reason | -| ---------- | ------ | -| u3002 | Feedback entry not found | - #### Get Summary -`(get-summary ((agent-id uint) (filter-client (optional principal)) (filter-tags (optional (list 10 (string-utf8 64))))) (response {count: uint, average-score: uint, total-score: uint} uint))` +`(get-summary ((agent-id uint) (client-addresses (list 200 principal)) (tag1 (string-utf8 64)) (tag2 (string-utf8 64))) (response {count: uint, summary-value: int, summary-value-decimals: uint} uint))` -Calculate aggregate reputation metrics for an agent, optionally filtered by client or tags. Returns the count of feedback entries, average score, and total score. +Calculate aggregate reputation metrics for an agent across specified clients and optional tag filters. All feedback values are normalized to WAD (18 decimals) for averaging, then scaled back to the mode (most common) decimals value among the feedback entries. Empty tag strings match all values. Returns empty summary if client-addresses is empty or no matching feedback exists. This method should be defined as read-only, i.e. `define-read-only`. #### Read All Feedback -`(read-all-feedback ((agent-id uint) (offset uint) (limit uint)) (response (list 50 {client: principal, index: uint, score: uint, tags: (list 10 (string-utf8 64)), revoked: bool}) uint))` +`(read-all-feedback ((agent-id uint) (opt-clients (optional (list 50 principal))) (opt-tag1 (optional (string-utf8 64))) (opt-tag2 (optional (string-utf8 64))) (include-revoked bool)) (response (list 50 {client: principal, index: uint, value: int, value-decimals: uint, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}) uint))` -Retrieve paginated feedback entries for an agent. Maximum limit is 50 entries per call. +Retrieve feedback entries for an agent with optional filters. If opt-clients is none, reads from all clients who have given feedback. Tag filters are optional. Maximum 50 entries returned. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Last Index + +`(get-last-index ((agent-id uint) (client principal)) (response uint uint))` + +Get the last feedback index submitted by a client for an agent. Returns 0 if no feedback has been submitted. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Clients + +`(get-clients ((agent-id uint)) (response (optional (list 1024 principal)) uint))` + +Get the list of all clients who have given feedback for an agent. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Approved Limit + +`(get-approved-limit ((agent-id uint) (client principal)) (response uint uint))` + +Get the approved index limit for a client. Returns 0 if no approval exists. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Response Count + +`(get-response-count ((agent-id uint) (opt-client (optional principal)) (opt-feedback-index (optional uint)) (opt-responders (optional (list 200 principal)))) (response uint uint))` + +Flexible response counting with optional filters. Can count responses across all clients, a specific client, a specific feedback entry, or specific responders. If opt-client is none, counts across all clients. If opt-feedback-index is none or 0, counts all feedback for the client(s). If opt-responders is provided, only counts responses from those principals. This method should be defined as read-only, i.e. `define-read-only`. #### Get Responders -`(get-responders ((agent-id uint) (index uint)) (response (list 10 principal) uint))` +`(get-responders ((agent-id uint) (client principal) (index uint)) (response (optional (list 256 principal)) uint))` Get the list of principals who have responded to a specific feedback entry. @@ -461,29 +503,44 @@ This method should be defined as read-only, i.e. `define-read-only`. ;; Pre-authorize a client to provide feedback (approve-client (uint principal uint) (response bool uint)) - ;; Submit feedback with on-chain approval - (give-feedback (uint uint (list 10 (string-utf8 64)) (string-utf8 512)) (response uint uint)) + ;; Submit feedback (permissionless) + (give-feedback (uint int uint (string-utf8 64) (string-utf8 64) (string-utf8 512) (string-utf8 512) (buff 32)) (response uint uint)) - ;; Submit feedback with SIP-018 signature - (give-feedback-signed (uint uint (list 10 (string-utf8 64)) (string-utf8 512) (buff 65) uint) (response uint uint)) + ;; Submit feedback (on-chain approval) + (give-feedback-approved (uint int uint (string-utf8 64) (string-utf8 64) (string-utf8 512) (string-utf8 512) (buff 32)) (response uint uint)) + + ;; Submit feedback (SIP-018 signature) + (give-feedback-signed (uint int uint (string-utf8 64) (string-utf8 64) (string-utf8 512) (string-utf8 512) (buff 32) principal uint uint (buff 65)) (response uint uint)) ;; Revoke submitted feedback (revoke-feedback (uint uint) (response bool uint)) ;; Respond to feedback - (append-response (uint uint (string-utf8 512)) (response bool uint)) + (append-response (uint principal uint (string-utf8 512) (buff 32)) (response bool uint)) ;; Read single feedback entry - (read-feedback (uint principal uint) (response {score: uint, tags: (list 10 (string-utf8 64)), feedback-uri: (string-utf8 512), revoked: bool, block-height: uint} uint)) + (read-feedback (uint principal uint) (response (optional {value: int, value-decimals: uint, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}) uint)) + + ;; Get aggregate reputation summary (WAD-normalized) + (get-summary (uint (list 200 principal) (string-utf8 64) (string-utf8 64)) (response {count: uint, summary-value: int, summary-value-decimals: uint} uint)) + + ;; Read filtered feedback + (read-all-feedback (uint (optional (list 50 principal)) (optional (string-utf8 64)) (optional (string-utf8 64)) bool) (response (list 50 {client: principal, index: uint, value: int, value-decimals: uint, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}) uint)) + + ;; Get last feedback index for a client + (get-last-index (uint principal) (response uint uint)) + + ;; Get all clients who gave feedback + (get-clients (uint) (response (optional (list 1024 principal)) uint)) - ;; Get aggregate reputation summary - (get-summary (uint (optional principal) (optional (list 10 (string-utf8 64)))) (response {count: uint, average-score: uint, total-score: uint} uint)) + ;; Get approved index limit for a client + (get-approved-limit (uint principal) (response uint uint)) - ;; Read paginated feedback - (read-all-feedback (uint uint uint) (response (list 50 {client: principal, index: uint, score: uint, tags: (list 10 (string-utf8 64)), revoked: bool}) uint)) + ;; Get response count with optional filters + (get-response-count (uint (optional principal) (optional uint) (optional (list 200 principal))) (response uint uint)) ;; Get responders to feedback - (get-responders (uint uint) (response (list 10 principal) uint)) + (get-responders (uint principal uint) (response (optional (list 256 principal)) uint)) ) ) ``` From 4a36a87a60cc151331033ccb172ca849aec5acb8 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:32:24 -0700 Subject: [PATCH 09/28] feat(sip-xxx): update validation registry to v2.0.0 spec - Add request-uri/response-uri parameters for off-chain data - Document progressive validation workflow (multi-call responses) - Add has-response field to track response status - Simplify get-summary tag filter from list to single optional string - Update all function signatures to match contract implementation - Fix read-only return types (remove error response wrappers) Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 56 +++++++++++------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 4816241f..153a814b 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -553,9 +553,9 @@ The Validation Registry enables third-party validators to approve or reject agen #### Validation Request -`(validation-request ((agent-id uint) (validator principal) (request-hash (buff 32))) (response bool uint))` +`(validation-request ((validator principal) (agent-id uint) (request-uri (string-utf8 512)) (request-hash (buff 32))) (response bool uint))` -Request validation from a specific validator. Only the agent owner or approved operator may initiate a request. The request-hash is a unique identifier for this validation request. +Request validation from a specific validator. Only the agent owner or approved operator may initiate a request. Creates an initial validation record with response=0 and has-response=false. The request-uri points to off-chain validation request details, and the request-hash is a unique identifier for this validation request. This method must be defined with `define-public`. @@ -568,9 +568,9 @@ This method must be defined with `define-public`. #### Validation Response -`(validation-response ((request-hash (buff 32)) (score uint) (response-hash (buff 32)) (tag (string-utf8 64))) (response bool uint))` +`(validation-response ((request-hash (buff 32)) (response uint) (response-uri (string-utf8 512)) (response-hash (buff 32)) (tag (string-utf8 64))) (response bool uint))` -Submit a validation response. Only the designated validator for the request may respond. Score must be between 0 and 100, where 0 indicates rejection and higher scores indicate varying levels of approval. +Submit a validation response. Only the designated validator for the request may respond. This function supports progressive validation and can be called multiple times to update the response score, response-uri, response-hash, and tag. The response score must be between 0 and 100, where 0 indicates rejection and higher scores indicate varying levels of approval. There is no monotonic requirement, so the response score can decrease in subsequent calls (e.g., from preliminary to final assessment). The response-uri points to off-chain validation response details. This method must be defined with `define-public`. @@ -578,47 +578,43 @@ This method must be defined with `define-public`. | ---------- | ------ | | u2000 | Caller is not the designated validator | | u2002 | Validation request not found | -| u2005 | Score exceeds maximum (100) | +| u2005 | Invalid response (exceeds 100) | #### Get Validation Status -`(get-validation-status ((request-hash (buff 32))) (response {validator: principal, agent-id: uint, score: uint, response-hash: (buff 32), tag: (string-utf8 64), last-update: uint} uint))` +`(get-validation-status ((request-hash (buff 32))) (optional {validator: principal, agent-id: uint, response: uint, response-hash: (buff 32), tag: (string-utf8 64), last-update: uint, has-response: bool}))` -Retrieve the status of a validation request. +Retrieve the status of a validation request. Returns none if the validation is not found. The has-response field indicates whether the validator has provided a response (false after validation-request, true after first validation-response call). The response field (renamed from score for clarity) contains the validation score. This method should be defined as read-only, i.e. `define-read-only`. -| error code | reason | -| ---------- | ------ | -| u2002 | Validation not found | - #### Get Summary -`(get-summary ((agent-id uint) (filter-validators (optional (list 10 principal))) (filter-tags (optional (list 10 (string-utf8 64))))) (response {count: uint, average-score: uint, total-score: uint} uint))` +`(get-summary ((agent-id uint) (opt-validators (optional (list 200 principal))) (opt-tag (optional (string-utf8 64)))) {count: uint, avg-response: uint})` -Calculate aggregate validation metrics for an agent, optionally filtered by validators or tags. +Calculate aggregate validation metrics for an agent, optionally filtered by validators and a single tag. The opt-tag parameter accepts a single optional string instead of a list - an empty string or none matches all tags. Only validations where has-response is true are counted in the aggregation. Returns a tuple with count (number of matching validations) and avg-response (average validation score), or {count: 0, avg-response: 0} if no matching validations exist. This method should be defined as read-only, i.e. `define-read-only`. #### Get Agent Validations -`(get-agent-validations ((agent-id uint)) (response (list 1024 (buff 32)) uint))` +`(get-agent-validations ((agent-id uint)) (optional (list 1024 (buff 32))))` -Get all validation request hashes for an agent. +Get all validation request hashes for an agent. Returns none if the agent has no validations. This method should be defined as read-only, i.e. `define-read-only`. #### Get Validator Requests -`(get-validator-requests ((validator principal)) (response (list 1024 (buff 32)) uint))` +`(get-validator-requests ((validator principal)) (optional (list 1024 (buff 32))))` -Get all validation request hashes assigned to a validator. +Get all validation request hashes assigned to a validator. Returns none if the validator has no requests. This method should be defined as read-only, i.e. `define-read-only`. #### Get Identity Registry -`(get-identity-registry () (response principal uint))` +`(get-identity-registry () principal)` Return the principal of the linked Identity Registry contract. @@ -626,7 +622,7 @@ This method should be defined as read-only, i.e. `define-read-only`. #### Get Version -`(get-version () (response (string-ascii 16) uint))` +`(get-version () (string-utf8 8))` Return the contract version string. @@ -638,28 +634,28 @@ This method should be defined as read-only, i.e. `define-read-only`. (define-trait validation-registry-trait ( ;; Request validation from a validator - (validation-request (uint principal (buff 32)) (response bool uint)) + (validation-request (principal uint (string-utf8 512) (buff 32)) (response bool uint)) - ;; Submit validation response - (validation-response ((buff 32) uint (buff 32) (string-utf8 64)) (response bool uint)) + ;; Submit validation response (progressive: can be called multiple times) + (validation-response ((buff 32) uint (string-utf8 512) (buff 32) (string-utf8 64)) (response bool uint)) - ;; Get validation status - (get-validation-status ((buff 32)) (response {validator: principal, agent-id: uint, score: uint, response-hash: (buff 32), tag: (string-utf8 64), last-update: uint} uint)) + ;; Get validation status (returns none if not found) + (get-validation-status ((buff 32)) (optional {validator: principal, agent-id: uint, response: uint, response-hash: (buff 32), tag: (string-utf8 64), last-update: uint, has-response: bool})) - ;; Get aggregate validation summary - (get-summary (uint (optional (list 10 principal)) (optional (list 10 (string-utf8 64)))) (response {count: uint, average-score: uint, total-score: uint} uint)) + ;; Get aggregate validation summary (only counts entries with has-response=true) + (get-summary (uint (optional (list 200 principal)) (optional (string-utf8 64))) {count: uint, avg-response: uint}) ;; Get all validations for an agent - (get-agent-validations (uint) (response (list 1024 (buff 32)) uint)) + (get-agent-validations (uint) (optional (list 1024 (buff 32)))) ;; Get all requests for a validator - (get-validator-requests (principal) (response (list 1024 (buff 32)) uint)) + (get-validator-requests (principal) (optional (list 1024 (buff 32)))) ;; Get linked identity registry - (get-identity-registry () (response principal uint)) + (get-identity-registry () principal) ;; Get contract version - (get-version () (response (string-ascii 16) uint)) + (get-version () (string-utf8 8)) ) ) ``` From 70311ab1926fc8b792d649077c917458b1ee279e Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:36:22 -0700 Subject: [PATCH 10/28] docs(sip-xxx): update cross-cutting sections for v2.0.0 Complete rewrite of Error Code Summary with all 28 v2.0.0 error codes (9 identity + 6 validation + 13 reputation), fix SIP-018 hash format documentation to show actual construction with SIP018_PREFIX and to-consensus-buff?, replace Agent Metadata schema with ERC-8004 registration file format including services/registrations/supportedTrust fields, and update Multichain Identity section to explain cross-chain registration via registrations array. Changes: - Error Code Summary: Added u1004-u1008 (identity), u3011-u3012 (reputation) with exact descriptions from contract constants - SIP-018 Integration: Document hash construction sha256(concat SIP018_PREFIX (concat domain-hash structured-data-hash)) with message structures for set-agent-wallet-signed and give-feedback-signed - Agent Registration File: Replace custom schema with ERC-8004 spec including type, name, description, image, services array (A2A/MCP/OASF/ENS/email), x402Support, active, registrations, supportedTrust fields - Multichain Identity: Explain registrations array for cross-chain presence, add multi-chain agent example with Stacks + Ethereum registrations All cross-cutting sections now accurately reflect v2.0.0 contracts and align with ERC-8004 multichain agent identity specification. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 + sips/sip-XXX/sip-XXX-agent-registries.md | 212 +++++++++++++++++++---- 2 files changed, 177 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 19d57931..22bef3cc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.rej .aider* +.planning/ diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 153a814b..4ac0d121 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -662,71 +662,107 @@ This method should be defined as read-only, i.e. `define-read-only`. ## Error Code Summary -### Identity Registry Errors (u1000-u1999) +This section lists all error codes used across the three registry contracts in v2.0.0. + +### Identity Registry Errors (u1000-u1008) | error code | reason | | ---------- | ------ | -| u1000 | Not authorized | +| u1000 | Not authorized (caller is not owner or approved operator) | | u1001 | Agent not found | | u1002 | Agent already exists | | u1003 | Metadata set failed | -| u1004 | Reserved key (agentWallet cannot be set via set-metadata) | +| u1004 | Reserved key (agentWallet cannot be set via set-metadata or during registration) | | u1005 | Invalid sender (tx-sender must match sender parameter in transfer) | | u1006 | Wallet already set (tx-sender is already the agent wallet) | | u1007 | Expired signature (deadline passed or exceeds MAX_DEADLINE_DELAY) | | u1008 | Invalid signature (recovery failed or doesn't match expected principal) | -### Validation Registry Errors (u2000-u2999) +### Validation Registry Errors (u2000-u2005) | error code | reason | | ---------- | ------ | -| u2000 | Not authorized | +| u2000 | Not authorized (caller is not owner, approved operator, or designated validator) | | u2001 | Agent not found | | u2002 | Validation not found | | u2003 | Validation already exists | -| u2004 | Invalid validator | -| u2005 | Invalid response score | +| u2004 | Invalid validator (cannot be self) | +| u2005 | Invalid response (exceeds 100) | -### Reputation Registry Errors (u3000-u3999) +### Reputation Registry Errors (u3000-u3012) | error code | reason | | ---------- | ------ | -| u3000 | Not authorized | +| u3000 | Not authorized (signer is not owner or approved operator) | | u3001 | Agent not found | | u3002 | Feedback not found | | u3003 | Feedback already revoked | -| u3004 | Invalid score (exceeds 100) | -| u3005 | Self-feedback not allowed | -| u3006 | Invalid index | +| u3004 | Invalid value (reserved, unused in v2.0.0) | +| u3005 | Self-feedback not allowed (caller is owner or approved operator) | +| u3006 | Invalid index (must be > 0) | | u3007 | Signature verification failed | | u3008 | Authorization expired | | u3009 | Index limit exceeded | -| u3010 | Empty feedback URI | +| u3010 | Empty response URI | +| u3011 | Invalid decimals (exceeds 18) | +| u3012 | Empty client list (reserved, unused in v2.0.0) | # Multichain Identity -This standard supports cross-chain agent identity using CAIP-2 [4] compliant identifiers. This enables agents registered on Stacks to be referenced from other chains and vice versa. +This standard supports cross-chain agent identity using CAIP-2 [4] compliant identifiers, enabling agents registered on Stacks to be referenced from other chains and vice versa. Agents advertise all their cross-chain registrations in the registration file's `registrations` array. ## Identifier Format +Each agent is uniquely identified globally using the CAIP-2 format: + ``` -stacks::: +::: ``` -Where: -- `stacks` is the namespace identifier +For Stacks agents: +- `` is `stacks` - `` is the chain identifier (1 for mainnet, 2147483648 for testnet) -- `` is the registry contract name +- `` is the fully-qualified registry contract principal - `` is the agent's numeric ID ## Chain Identifiers | Network | Chain ID | Example | | ------- | -------- | ------- | -| Stacks Mainnet | 1 | `stacks:1:identity-registry:42` | -| Stacks Testnet | 2147483648 | `stacks:2147483648:identity-registry:0` | +| Stacks Mainnet | 1 | `stacks:1:SP000000000000000000002Q6VF78.identity-registry:42` | +| Stacks Testnet | 2147483648 | `stacks:2147483648:ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.identity-registry:0` | + +## Cross-Chain Registration + +Agents can be registered on multiple blockchains and advertise all registrations in their registration file's `registrations` array. Each entry contains: + +```json +{ + "agentId": 42, + "agentRegistry": "stacks:1:SP000000000000000000002Q6VF78.identity-registry" +} +``` + +### Example: Multi-Chain Agent -This format aligns with ERC-8004 [3] multichain identifiers, enabling consistent agent identity across Ethereum, Stacks, and other supported chains. +An agent registered on both Stacks mainnet (agent ID 42) and Ethereum mainnet (agent ID 123) would list both in the registration file: + +```json +{ + "registrations": [ + { + "agentId": 42, + "agentRegistry": "stacks:1:SP000000000000000000002Q6VF78.identity-registry" + }, + { + "agentId": 123, + "agentRegistry": "eip155:1:0x742d35Cc6634C0532925a3b844Bc454e4438f44e" + } + ] +} +``` + +This enables applications on any chain to discover the agent's presence on other chains and aggregate reputation signals across the entire network. For complete details on cross-chain agent identity, see ERC-8004 [3]. # Implementing in Wallets and Applications @@ -736,34 +772,138 @@ Developers building applications that interact with agent registries should foll Before interacting with a registry contract, applications should verify that the contract implements the appropriate trait. This can be done by checking the contract's interface or attempting to call read-only functions. -## Agent Metadata +## Agent Registration File + +The URI returned by `get-uri` (or `get-token-uri` for SIP-009 compatibility) points to the agent registration file. The URI may use any scheme: `ipfs://` (e.g., `ipfs://QmHash`), `https://` (e.g., `https://example.com/agent.json`), or a base64-encoded `data:` URI (e.g., `data:application/json;base64,eyJ0eXBlIjoi...`) for fully on-chain metadata. -The URI returned by `get-uri` should point to a JSON file with the following recommended schema: +The registration file MUST follow the ERC-8004 [3] registration file structure to ensure cross-chain compatibility: ```json { + "type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1", "name": "Agent Name", "description": "Description of the agent's purpose and capabilities", "image": "https://example.com/agent-avatar.png", - "capabilities": ["capability1", "capability2"], - "endpoints": { - "api": "https://api.example.com/agent", - "websocket": "wss://ws.example.com/agent" - }, - "version": "1.0.0" + "services": [ + { + "name": "web", + "endpoint": "https://web.agentxyz.com/" + }, + { + "name": "A2A", + "endpoint": "https://agent.example/.well-known/agent-card.json", + "version": "0.3.0" + }, + { + "name": "MCP", + "endpoint": "https://mcp.agent.eth/", + "version": "2025-06-18" + }, + { + "name": "OASF", + "endpoint": "ipfs://{cid}", + "version": "0.8", + "skills": [], + "domains": [] + }, + { + "name": "ENS", + "endpoint": "agent.eth", + "version": "v1" + }, + { + "name": "email", + "endpoint": "mail@agent.com" + } + ], + "x402Support": false, + "active": true, + "registrations": [ + { + "agentId": 42, + "agentRegistry": "stacks:1:SP000000000000000000002Q6VF78.identity-registry" + } + ], + "supportedTrust": [ + "reputation", + "validation" + ] } ``` +### Required Fields + +- `type`: MUST be `"https://eips.ethereum.org/EIPS/eip-8004#registration-v1"` +- `name`: Human-readable agent name +- `description`: Natural language description of the agent's purpose and capabilities +- `image`: URI pointing to agent avatar image + +### Optional Fields + +- `services`: Array of service endpoints (A2A, MCP, OASF, ENS, DID, web, email, etc.) +- `x402Support`: Boolean indicating support for x402 payment protocol +- `active`: Boolean indicating agent operational status +- `registrations`: Array of on-chain registrations across multiple blockchains (see Multichain Identity section) +- `supportedTrust`: Array of trust models supported by the agent (e.g., `"reputation"`, `"validation"`, `"crypto-economic"`, `"tee-attestation"`) + +The `services` array allows agents to advertise multiple communication endpoints. Each service entry contains a `name`, `endpoint`, and optional `version`. The flexibility of this structure enables agents to support emerging protocols while maintaining backward compatibility with existing standards. + ## SIP-018 Signature Integration -For `give-feedback-signed`, the structured data domain and message should follow SIP-018 [1] conventions. The message should include: -- Agent ID -- Score -- Tags -- Feedback URI -- Expiry block height +This standard uses SIP-018 [1] signed structured data for two operations: `set-agent-wallet-signed` in the Identity Registry and `give-feedback-signed` in the Reputation Registry. + +### Hash Construction + +The SIP-018 message hash is constructed as follows: + +```clarity +(sha256 (concat SIP018_PREFIX (concat domain-hash structured-data-hash))) +``` + +Where: +- `SIP018_PREFIX` is `0x534950303138` (the ASCII string "SIP018" in hexadecimal) +- `domain-hash` is `sha256(to-consensus-buff? domain)` where domain is `{name: (string-ascii 64), version: (string-ascii 64), chain-id: uint}` +- `structured-data-hash` is `sha256(to-consensus-buff? message)` where message contains the function-specific fields +- `to-consensus-buff?` is Clarity's native serialization format (analogous to EIP-712's typed data encoding) + +### Set Agent Wallet Message + +For `set-agent-wallet-signed`, the message structure is: + +```clarity +{ + agent-id: uint, + new-wallet: principal, + owner: principal, + deadline: uint +} +``` + +The signature must be produced by the `new-wallet` principal to prove wallet ownership. + +### Give Feedback Signed Message + +For `give-feedback-signed`, the message structure is: + +```clarity +{ + agent-id: uint, + client: principal, + index-limit: uint, + expiry: uint, + signer: principal +} +``` + +The signature must be produced by the `signer` (agent owner or approved operator) to authorize the client to submit feedback up to the specified index limit before the expiry block height. + +### Wallet Implementation -Wallets implementing feedback functionality should provide clear UI for users to understand what they are signing. +Wallets implementing SIP-018 signing for agent registries should: +- Display the domain (contract name, version, chain ID) prominently +- Show all message fields in human-readable format +- Warn users about deadline/expiry constraints +- Use `secp256k1-recover?` for signature verification with the message hash as input ## Use Cases and Common Patterns From 91071a88d8a533f1de3df013e8fe0d433d75db6a Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:41:03 -0700 Subject: [PATCH 11/28] docs(sip-xxx): add authorization model and security considerations Add comprehensive documentation for v2.0.0 authorization patterns and security concerns: ## Authorization Model Section - Document tx-sender vs contract-caller security model (prevents delegation attacks) - Explain is-authorized-or-owner cross-contract authorization pattern - Detail agent wallet system with reserved "agentWallet" metadata key - Describe set-agent-wallet-direct (tx-sender proof) vs set-agent-wallet-signed (SIP-018) - Document automatic wallet clearing on NFT transfer - Explain deadline validation (MAX_DEADLINE_DELAY = 1500 blocks) ## Security Considerations Section - Sybil attacks on permissionless feedback: mitigations include curated client lists, meta-reputation, economic barriers - On-chain URI pointers vs full storage: content-addressed IPFS recommended, hash verification for mutable URIs - Validator incentive alignment: staking wrappers, meta-validation, multi-validator aggregation recommended - tx-sender authorization trade-offs: prevents impersonation but limits delegation (use set-approval-for-all) - Agent wallet security: auto-clear on transfer, deadline replay protection, chain-id binding, key rotation All patterns reference actual v2.0.0 contract implementations. Co-Authored-By: Claude Opus 4.6 --- sips/sip-XXX/sip-XXX-agent-registries.md | 355 +++++++++++++++++++++++ 1 file changed, 355 insertions(+) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 4ac0d121..de270bae 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -905,6 +905,183 @@ Wallets implementing SIP-018 signing for agent registries should: - Warn users about deadline/expiry constraints - Use `secp256k1-recover?` for signature verification with the message hash as input +## Authorization Model + +The v2.0.0 contracts implement a strict authorization model based on transaction sender identity and cross-contract authorization checks. This section documents the key patterns used throughout the registry implementations. + +### tx-sender vs contract-caller + +All ownership and authorization checks in v2.0.0 use `tx-sender` exclusively, never `contract-caller`. This is a critical security decision that prevents delegation attacks. + +**Why tx-sender?** + +Using `tx-sender` ensures that only the actual transaction originator can perform privileged operations. If contracts used `contract-caller` instead, a malicious intermediary contract could impersonate the owner by calling registry functions on behalf of users, even without explicit permission. + +**Example from identity-registry.clar:** + +```clarity +(define-public (set-agent-uri (agent-id uint) (new-uri (string-utf8 512))) + (begin + (asserts! (is-authorized agent-id tx-sender) ERR_NOT_AUTHORIZED) + (map-set uris {agent-id: agent-id} new-uri) + ;; ... + ) +) +``` + +The `is-authorized` helper checks if `tx-sender` (not `contract-caller`) is either the NFT owner or an approved operator: + +```clarity +(define-private (is-authorized (agent-id uint) (caller principal)) + (let ( + (owner-opt (nft-get-owner? agent-identity agent-id)) + ) + (match owner-opt owner + (or + (is-eq caller owner) + (is-approved-for-all agent-id caller) + ) + false + ) + ) +) +``` + +**Trade-off:** This approach prevents delegation through intermediary contracts. For delegated operations, owners must use `set-approval-for-all` to explicitly grant operator permissions instead of relying on contract-level delegation. + +### is-authorized-or-owner Pattern + +The Identity Registry exposes a public read-only function for cross-contract authorization checks: + +```clarity +(define-read-only (is-authorized-or-owner (spender principal) (agent-id uint)) + (let ( + (owner (unwrap! (nft-get-owner? agent-identity agent-id) ERR_AGENT_NOT_FOUND)) + ) + (ok (or + (is-eq spender owner) + (is-approved-for-all agent-id spender) + )) + ) +) +``` + +This function returns `(response bool uint)`: +- `(ok true)` if the spender is authorized (owner or approved operator) +- `(ok false)` if the spender is not authorized +- `(err u1001)` if the agent does not exist + +**Usage in reputation-registry.clar:** + +The Reputation Registry uses this function to prevent self-feedback. In the permissionless `give-feedback` function: + +```clarity +(let ( + (auth-check (contract-call? .identity-registry is-authorized-or-owner caller agent-id)) +) + ;; Verify agent exists (is-authorized-or-owner returns error if not) + (asserts! (is-ok auth-check) ERR_AGENT_NOT_FOUND) + ;; Verify caller is NOT authorized (prevent self-feedback) + (asserts! (not (unwrap-panic auth-check)) ERR_SELF_FEEDBACK) + ;; ... continue with feedback submission +) +``` + +This pattern enables composable authorization across registry contracts while maintaining the tx-sender security model. + +### Agent Wallet System + +The Identity Registry includes a special metadata system for agent wallets through the reserved key `"agentWallet"`. This enables agents to control a separate wallet from their owner account—useful when the owner is a multisig or custody solution, but the agent needs a hot wallet for rapid transactions. + +**Reserved Key Protection:** + +The `"agentWallet"` key cannot be set via the standard `set-metadata` function or included in the `metadata-entries` array during `register-full`. Attempting to do so returns error u1004 (ERR_RESERVED_KEY). + +**Automatic Initialization:** + +When an agent is registered, the agent wallet is automatically set to the owner's address: + +```clarity +(define-public (register-full + (token-uri (string-utf8 512)) + (metadata-entries (list 10 {key: (string-utf8 128), value: (buff 512)})) +) + (let ((agent-id (var-get next-agent-id)) (owner tx-sender)) + ;; ... mint NFT and set URI ... + ;; Auto-set agent-wallet to owner + (map-set agent-wallets {agent-id: agent-id} owner) + ;; ... + ) +) +``` + +**Transfer Behavior:** + +When an agent identity NFT is transferred via the `transfer` function, the agent wallet is automatically cleared to prevent stale wallet associations: + +```clarity +(define-public (transfer (token-id uint) (sender principal) (recipient principal)) + (begin + (asserts! (is-eq tx-sender sender) ERR_INVALID_SENDER) + ;; ... authorization checks ... + ;; Clear agent wallet before transfer + (map-delete agent-wallets {agent-id: token-id}) + (try! (nft-transfer? agent-identity token-id sender recipient)) + ;; ... + ) +) +``` + +The new owner must then re-verify wallet ownership using one of the update methods below. + +**Update Methods:** + +Two functions allow updating the agent wallet, each proving wallet ownership differently: + +1. **Direct Update (Transaction Signature):** + + ```clarity + (define-public (set-agent-wallet-direct (agent-id uint)) + (let ((owner (unwrap! (nft-get-owner? agent-identity agent-id) ERR_AGENT_NOT_FOUND))) + ;; Check caller is authorized (owner or approved operator) + (asserts! (is-authorized agent-id tx-sender) ERR_NOT_AUTHORIZED) + ;; ... check wallet not already set to tx-sender ... + ;; Set new wallet + (map-set agent-wallets {agent-id: agent-id} tx-sender) + (ok true) + ) + ) + ``` + + The wallet proves ownership by being the transaction sender (`tx-sender`). Only the agent owner or an approved operator can initiate this call, but the wallet being set is always `tx-sender`. + +2. **Signed Update (SIP-018 Signature):** + + ```clarity + (define-public (set-agent-wallet-signed + (agent-id uint) + (new-wallet principal) + (deadline uint) + (signature (buff 65)) + ) + (let ((owner (unwrap! (nft-get-owner? agent-identity agent-id) ERR_AGENT_NOT_FOUND))) + ;; Authorization check (caller must be owner or operator) + (asserts! (is-authorized agent-id tx-sender) ERR_NOT_AUTHORIZED) + ;; Deadline validation + (asserts! (<= current-height deadline) ERR_EXPIRED_SIGNATURE) + (asserts! (<= deadline (+ current-height MAX_DEADLINE_DELAY)) ERR_EXPIRED_SIGNATURE) + ;; Verify signature from new-wallet + ;; ... SIP-018 signature verification ... + (map-set agent-wallets {agent-id: agent-id} new-wallet) + (ok true) + ) + ) + ``` + + The new wallet proves ownership by providing a valid SIP-018 signature. The deadline must be the current block height or future, but within MAX_DEADLINE_DELAY (1500 blocks, approximately 5 minutes at 200s block times). This prevents replay attacks while allowing reasonable clock skew. + +The agent wallet can be read via `get-agent-wallet` (returns `(optional principal)`) and removed via `unset-agent-wallet`. + ## Use Cases and Common Patterns The following patterns illustrate how agent registries can be used across applications: @@ -927,6 +1104,184 @@ When displaying agent reputation: - Display recent feedback with associated tags for context - Consider filtering by validation status for quality assurance +# Security Considerations + +This section outlines potential security risks and recommended mitigations for implementations of the agent registry standard. + +## Sybil Attacks on Permissionless Feedback + +The Reputation Registry allows permissionless feedback by default—any principal can submit feedback for any agent without prior authorization. While this openness enables broad participation, it creates vulnerability to Sybil attacks where a single entity creates multiple client accounts to artificially inflate or deflate an agent's reputation. + +**Attack Scenario:** + +An attacker controls 100 principals and submits positive feedback from all of them to boost their own agent's reputation, or negative feedback to harm a competitor. + +**Built-in Protections:** + +1. **Self-feedback prevention**: The `give-feedback` function uses `is-authorized-or-owner` to verify the caller is not the agent owner or an approved operator. This prevents the simplest form of self-promotion. + +2. **Three authorization paths**: Agents can choose their feedback model: + - **Permissionless** (`give-feedback`): Anyone can submit feedback except the owner/operators + - **On-chain approval** (`give-feedback-approved`): Only pre-authorized clients can submit feedback + - **SIP-018 signed** (`give-feedback-signed`): Off-chain authorization with cryptographic signatures + +**Recommended Mitigations:** + +- **Application-level filtering**: Applications SHOULD filter feedback by trusted client lists rather than accepting all feedback equally. For example, only show feedback from clients who have been validated, have established reputations themselves, or have paid bonds. + +- **Meta-reputation systems**: Implement reputation-of-raters where client credibility is tracked. Weight feedback from high-reputation clients more heavily than unknown clients. + +- **Economic barriers**: Require clients to stake tokens or pay fees to submit feedback, making Sybil attacks economically infeasible at scale. + +- **Curated feedback**: Agents concerned about spam should use `approve-client` to pre-authorize trusted clients with index limits, or use `give-feedback-signed` for full off-chain control. + +- **Temporal analysis**: Monitor feedback submission patterns. Sudden bursts of feedback from new clients should be flagged for review. + +## On-Chain URI Pointers vs Full Data Storage + +The registry contracts store URI pointers (strings up to 512 characters) rather than full data on-chain. This design choice optimizes for gas costs and flexibility but introduces availability and mutability risks. + +**Design Rationale:** + +Storing full agent registration files, feedback details, or validation reports on-chain would be prohibitively expensive and inflexible. Large JSON files would cost thousands of dollars in transaction fees and couldn't be updated without redeploying contracts. + +**Risks:** + +1. **URI target disappearance**: A URI pointing to `https://example.com/agent.json` will fail if the server goes offline or the domain expires. + +2. **URI target mutation**: Centrally-hosted URIs can be changed after registration, potentially misleading users who expect immutable records. + +3. **Censorship**: Centralized hosting providers can remove content or block access. + +**Recommended Mitigations:** + +- **Content-addressed storage**: Use IPFS URIs with CID (Content Identifier) encoding. Example: `ipfs://QmX7M9CiYXjVeFnrSUdREuH3QdFkX3vKkMcfLH2RuH7RjN`. The CID cryptographically guarantees content integrity—if the content changes, the CID changes. + +- **Hash verification**: For non-content-addressed URIs, include hash fields (`feedback-hash`, `response-hash`, `request-hash`) in function calls. Applications should verify the hash matches the fetched content. + +- **Redundant storage**: Mirror critical URIs across multiple storage providers (IPFS, Arweave, Filecoin, centralized backups). + +- **On-chain fallback**: For critical metadata, use base64-encoded `data:` URIs to store content directly on-chain: `data:application/json;base64,eyJ0eXBlIjoi...` + +- **Monitoring services**: Implement URI availability monitoring and alert systems for broken links. + +**Current Implementation:** + +- `identity-registry`: Stores `token-uri` (512 char string) +- `reputation-registry`: Stores feedback values on-chain, emits `feedback-uri` in events (not stored) +- `validation-registry`: Stores `request-uri` and `response-uri` (512 char strings each) + +## Validator Incentive Alignment + +The Validation Registry provides a standard interface for third-party validators to submit verification responses but does not include built-in economic incentives. This creates a free-rider problem where validators lack motivation to provide accurate, timely responses. + +**Problem:** + +Without staking, bonding, or payment mechanisms, validators can: +- Submit inaccurate responses with no penalty +- Delay responses indefinitely +- Collude with agents to provide favorable scores +- Abandon requests after accepting them + +**Progressive Validation Design:** + +The v2.0.0 `validation-response` function allows validators to submit multiple updates per request (preliminary → final scores) without monotonic constraints. This supports iterative validation workflows but also enables validators to change scores arbitrarily. + +**Recommended Mitigations:** + +1. **Wrapper contracts with staking**: Deploy proxy contracts that require validators to stake tokens before accepting requests. Slashing conditions can penalize late or inaccurate responses. + +2. **Meta-validation**: Track validator accuracy by comparing responses across multiple validators for the same agent. Validators with poor accuracy records should be deprioritized. + +3. **Multi-validator aggregation**: Request validation from multiple independent validators and aggregate scores (median, weighted average). This reduces reliance on any single validator's honesty. + +4. **Validator bonds**: Implement challenge-response systems where validators post bonds that can be forfeited if their responses are successfully disputed. + +5. **Reputation of validators**: Maintain validator reputation scores based on historical accuracy, response time, and client satisfaction. Applications should filter validators by reputation. + +6. **Payment integration**: Integrate with payment protocols (x402, direct transfers) to compensate validators for work. Payments can be released upon completion or held in escrow subject to dispute resolution. + +**Example Staking Wrapper Pattern:** + +```clarity +(define-public (request-validation-with-bond (validator principal) (agent-id uint) ...) + (begin + ;; Require validator to have staked minimum bond + (asserts! (>= (get-validator-stake validator) MIN_STAKE) ERR_INSUFFICIENT_STAKE) + ;; Forward request to validation-registry + (contract-call? .validation-registry validation-request validator agent-id ...) + ;; Lock validator stake until response or timeout + ) +) +``` + +## tx-sender Authorization and Delegation + +The exclusive use of `tx-sender` for authorization checks provides strong security guarantees but limits delegation patterns. + +**Security Benefit:** + +By checking `tx-sender` instead of `contract-caller`, the registries prevent intermediary contract attacks. A malicious contract cannot impersonate the owner by forwarding calls, because `tx-sender` always represents the transaction originator, not the immediate caller. + +**Example Attack (prevented by tx-sender):** + +If contracts used `contract-caller`: +1. Alice owns agent #42 +2. Bob deploys a malicious contract that calls `set-agent-uri(42, "evil-uri")` +3. Bob tricks Alice into calling his contract for an unrelated purpose +4. The malicious contract's call to `set-agent-uri` would have `contract-caller = malicious-contract` +5. If authorization checked `contract-caller`, the call would fail (good) +6. But if the malicious contract first called another intermediary that then called the registry, `contract-caller` could be spoofed + +Using `tx-sender` prevents this entire class of attacks because `tx-sender` always equals Alice regardless of call chain depth. + +**Trade-off:** + +The downside is that delegation through smart contracts is not supported. For example, a DAO contract cannot directly manage agents on behalf of members. However, the `set-approval-for-all` function provides an alternative: owners can explicitly grant operator permissions to contracts, enabling controlled delegation. + +**Recommendation:** + +For DAO or multisig agent management, use `set-approval-for-all` to grant the DAO/multisig contract operator privileges. This provides explicit, revocable delegation without compromising the tx-sender security model. + +## Agent Wallet Security + +The agent wallet system introduces additional security considerations around key management and transfer handling. + +**Automatic Clearing on Transfer:** + +When an agent identity NFT is transferred, the `agentWallet` metadata is automatically cleared (set to none). This prevents stale wallet associations where the old owner retains payment rights after transferring ownership. + +**Example scenario (mitigated):** +1. Alice owns agent #42 with wallet set to 0xABC +2. Alice transfers agent #42 to Bob +3. Without automatic clearing, 0xABC would still receive payments intended for Bob +4. With automatic clearing, Bob must re-verify a new wallet, ensuring payment control matches ownership + +**Deadline Validation:** + +The `set-agent-wallet-signed` function requires a `deadline` parameter that must satisfy: +- `deadline >= stacks-block-height` (not expired) +- `deadline <= stacks-block-height + MAX_DEADLINE_DELAY` (not too far in future) + +Where `MAX_DEADLINE_DELAY = 1500` blocks (approximately 5 minutes at 200-second block times). + +**Purpose:** +- **Replay attack prevention**: Signatures expire after the deadline, preventing old signatures from being reused +- **Clock skew tolerance**: The future deadline allows for reasonable network propagation delays +- **Bounded validity**: MAX_DEADLINE_DELAY prevents signatures from being valid indefinitely + +**Chain-ID Binding:** + +SIP-018 signatures include the `chain-id` in the domain hash, preventing cross-chain replay attacks. A signature valid on testnet (chain-id 2147483648) cannot be replayed on mainnet (chain-id 1). + +**Key Rotation Recommendation:** + +Applications should encourage users to rotate agent wallet keys regularly, especially for high-value agents. Since `set-agent-wallet-signed` signatures expire quickly (1500 blocks maximum), there's no long-term signature reuse risk, but compromised keys should still be rotated immediately. + +**Wallet Separation Benefits:** + +The agent wallet system's primary security benefit is separation of concerns: the owner key (which holds the NFT) can be a cold wallet or multisig, while the agent wallet can be a hot wallet used for frequent micropayments or protocol interactions. This limits exposure if the agent wallet is compromised—the attacker gains payment routing but not identity ownership. + # Related Work ## ERC-8004 From 100dea5fe0075b6d8d1716304cbcd311e3fb05dc Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:43:42 -0700 Subject: [PATCH 12/28] docs(sip-XXX): update Related Work with v2.0.0 cross-chain implementations Expanded Related Work section to accurately reflect v2.0.0 architecture: - Updated ERC-8004 subsection to document key differences (SIP-009 NFT trait for transferable agents, signed values with decimals, agent wallet system, tx-sender authorization model) - Added new Solana Program (s8004) subsection documenting Anchor implementation with PDA storage model and Borsh serialization - Added Cross-Chain Agent Identity subsection explaining registration file format support across all three implementations (Ethereum/Stacks/Solana) - Fixed "agents are not transferable" outdated statement from v1 - Added CAIP-2 multichain identifier references for cross-chain compatibility Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 33 +++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index de270bae..6c315075 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -1284,19 +1284,34 @@ The agent wallet system's primary security benefit is separation of concerns: th # Related Work -## ERC-8004 +## ERC-8004 (Ethereum/Solidity) -This SIP is designed to be compatible with ERC-8004 [3], the Agent Commerce Protocol on Ethereum. Both standards define the same three registries (Identity, Reputation, Validation) with equivalent functionality, enabling cross-chain agent identity. +This SIP is designed to be compatible with ERC-8004 [3], the Agent Commerce Protocol on Ethereum. Both standards define the same three registries (Identity, Reputation, Validation) with equivalent functionality, enabling cross-chain agent identity via CAIP-2 [4] identifiers. -Key differences in the Stacks implementation: -- Uses Clarity traits instead of Solidity interfaces -- Leverages SIP-018 [1] for signed structured data instead of EIP-712 -- Uses direct ownership maps instead of ERC-721 for identity (agents are not transferable) -- Follows SIP-019 [2] notification patterns for events +Key differences in the Stacks v2.0.0 implementation: +- **Traits vs Interfaces**: Uses Clarity traits instead of Solidity interfaces +- **NFT Standard**: Implements SIP-009 [5] NFT trait for agent identities (transferable, standard wallet/explorer integration) instead of ERC-721 +- **Signed Data**: Leverages SIP-018 [1] for signed structured data instead of EIP-712 +- **Signed Values**: Uses signed integer values with decimals (0-18) for reputation scores, supporting negative feedback and WAD normalization +- **Agent Wallets**: Includes reserved metadata system for agent-controlled wallets separate from owner accounts +- **Authorization Model**: Strict tx-sender authorization model (prevents delegation attacks) with optional operator approval +- **Event Patterns**: Follows SIP-019 [2] notification patterns for token metadata updates -## Other Agent Standards +The Ethereum implementation is available at https://github.com/erc8004-org/erc8004-contracts. -Various blockchain projects have proposed agent identity solutions, but most focus solely on identity without integrated reputation and validation. This standard's three-registry approach provides a more complete foundation for agent commerce. +## Solana Program (s8004) + +A Solana Anchor implementation of ERC-8004 is available at https://github.com/Woody4618/s8004. The Solana program uses Account-based storage (PDAs) and Borsh serialization instead of map-based storage and Clarity's consensus buffers. Like the Stacks implementation, it supports CAIP-2 multichain identifiers using the format `solana:::`. + +Key architectural differences from Stacks: +- **Account Model**: Agents stored in Program Derived Accounts (PDAs) instead of NFT tokens +- **Rent**: Requires rent-exempt balance for account storage (Solana's economic model) +- **Concurrency**: Parallel transaction processing enabled by explicit account dependencies +- **Serialization**: Uses Borsh instead of Clarity's consensus buffers + +## Cross-Chain Agent Identity + +All three implementations (Ethereum, Stacks, Solana) support the ERC-8004 registration file format, which includes a `registrations` array for tracking agent presence across multiple blockchains. This enables agents to maintain a single canonical metadata file while proving ownership of identities on multiple chains. Applications can resolve agent identities by fetching the registration file URI and validating on-chain ownership for each listed registration. # Backwards Compatibility From 216917812bb3d9e9123aae550ae926cc38954f7a Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:43:53 -0700 Subject: [PATCH 13/28] docs(sip-XXX): review and confirm Activation criteria for v2.0.0 Reviewed Activation section criteria: 1. Trait definitions deployed to mainnet 2. Complete implementation of all three registries on mainnet 3. Implementation passes reference test cases Criteria remain appropriate for v2.0.0. No changes needed - activation is correctly scoped to trait standard compliance rather than specific implementation features. Co-Authored-By: Claude From f145a0e98ab1ff97790a594d425f4fbd040228b3 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:44:09 -0700 Subject: [PATCH 14/28] docs(sip-XXX): update Reference Implementation links and verify testnet addresses Updated Reference Implementations section: - Fixed Ethereum GitHub org from erc-8004 to erc8004-org - Added Solana (Anchor) implementation link to Woody4618/s8004 - Verified testnet deployment addresses match current v2.0.0 deployments (ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.*) - Confirmed mainnet placeholder text remains appropriate All three multichain implementations now documented in source references. Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 6c315075..d115251c 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -1331,7 +1331,8 @@ This SIP will be considered activated when: Reference implementations are available at: - Stacks (Clarity): https://github.com/aibtcdev/erc-8004-stacks -- Ethereum (Solidity): https://github.com/erc-8004/erc-8004-contracts +- Ethereum (Solidity): https://github.com/erc8004-org/erc8004-contracts +- Solana (Anchor): https://github.com/Woody4618/s8004 ## Testnet Deployments From cfbbd7d51a6d2c69021f78392fca2951e54331e9 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:44:26 -0700 Subject: [PATCH 15/28] docs(sip-XXX): add SIP-009 to References section Added SIP-009 (Standard NFT Trait) as reference [5] to document the NFT standard used by v2.0.0 Identity Registry. This is a critical reference as agent identities are now NFT tokens with standard transfer, ownership query, and URI functions. All reference numbers remain correct (SIP-018, SIP-019, ERC-8004, CAIP-2, SIP-009). References are now complete for v2.0.0 specification. Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index d115251c..7de7c40d 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -1355,3 +1355,5 @@ Mainnet deployment addresses will be added upon activation. [3] ERC-8004: Agent Commerce Protocol - https://eips.ethereum.org/EIPS/eip-8004 [4] CAIP-2: Blockchain ID Specification - https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md + +[5] SIP-009: Standard NFT Trait - https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md From 5fed944f134d108b6a2cfdedb3b90e2561e6a716 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 6 Feb 2026 09:44:51 -0700 Subject: [PATCH 16/28] docs(sip-XXX): final version sweep for v2.0.0 consistency Performed comprehensive version consistency check: - Verified no stale v1 contract references (only legitimate ERC-8004 registration-v1 format and service version references) - Confirmed v2.0.0 is used consistently in all technical sections (Authorization Model, Error Codes, Related Work) - Verified all "non-transferable" language removed (agents are now transferable NFTs via SIP-009) - Confirmed Abstract accurately summarizes all v2.0.0 features (SIP-009 NFT, signed values, WAD normalization, agent wallets, three authorization paths, progressive validation) Document is version-consistent and ready for review. Co-Authored-By: Claude From 0b0ff3931ce32f8af1964ed963fbf75a1a92f474 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 7 Feb 2026 07:13:13 -0700 Subject: [PATCH 17/28] docs(sip-XXX): update Identity Registry trait to match v2 contracts Replace the Identity Registry trait code block with the actual trait definition from identity-registry-trait-v2.clar. Add design rationale explaining that traits only include response-wrapped functions, as Clarity traits cannot enforce raw return types like (optional principal) or bool. The updated trait includes only public state-changing functions and the response-wrapped read-only functions (SIP-009 + is-authorized-or-owner). Read-only functions with raw return types (owner-of, get-uri, get-metadata, is-approved-for-all, get-agent-wallet, get-version) remain part of the implementation but exist outside the trait definition. Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 53 +++++++----------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 7de7c40d..c09e1050 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -276,64 +276,43 @@ This method should be defined as read-only, i.e. `define-read-only`. ### Identity Registry Trait Implementation +**Design Rationale:** The trait only includes public state-changing functions and response-wrapped read-only functions (SIP-009 trait functions + `is-authorized-or-owner`). Clarity traits can only enforce function signatures that return `(response ...)` types. Read-only functions that return raw types like `(optional principal)`, `bool`, or `(string-utf8 8)` (`owner-of`, `get-uri`, `get-metadata`, `is-approved-for-all`, `get-agent-wallet`, `get-version`) are not included in the trait because Clarity cannot enforce their signatures via the trait system. These functions are still part of the implementation contract but exist outside the trait definition. + ```clarity +;; title: identity-registry-trait +;; version: 2.0.0 +;; summary: Trait definition for ERC-8004 Identity Registry +;; description: Defines the interface for identity registry contracts. Includes all public state-changing functions and response-wrapped read-only functions (SIP-009 + is-authorized-or-owner). + (define-trait identity-registry-trait ( - ;; Register a new agent with empty URI + ;; Registration functions (register () (response uint uint)) - - ;; Register a new agent with URI (register-with-uri ((string-utf8 512)) (response uint uint)) - - ;; Register a new agent with URI and metadata (register-full ((string-utf8 512) (list 10 {key: (string-utf8 128), value: (buff 512)})) (response uint uint)) - ;; Update agent URI + ;; Metadata management (set-agent-uri (uint (string-utf8 512)) (response bool uint)) - - ;; Set metadata key-value pair (set-metadata (uint (string-utf8 128) (buff 512)) (response bool uint)) - ;; Grant or revoke operator permissions + ;; Approval management (set-approval-for-all (uint principal bool) (response bool uint)) - ;; Set agent wallet to tx-sender + ;; Agent wallet management (set-agent-wallet-direct (uint) (response bool uint)) - - ;; Set agent wallet with SIP-018 signature (set-agent-wallet-signed (uint principal uint (buff 65)) (response bool uint)) - - ;; Remove agent wallet (unset-agent-wallet (uint) (response bool uint)) - ;; Transfer agent identity NFT + ;; NFT transfer (SIP-009) (transfer (uint principal principal) (response bool uint)) - ;; Get agent owner (legacy) - (owner-of (uint) (optional principal)) - - ;; Get agent URI - (get-uri (uint) (optional (string-utf8 512))) - - ;; Get metadata value for key - (get-metadata (uint (string-utf8 128)) (optional (buff 512))) - - ;; Check operator approval - (is-approved-for-all (uint principal) bool) - - ;; Get agent wallet - (get-agent-wallet (uint) (optional principal)) - - ;; Check if spender is authorized or owner - (is-authorized-or-owner (principal uint) (response bool uint)) - - ;; Get contract version - (get-version () (string-utf8 8)) - - ;; SIP-009 NFT trait functions + ;; SIP-009 trait functions (response-wrapped read-only) (get-last-token-id () (response uint uint)) (get-token-uri (uint) (response (optional (string-utf8 512)) uint)) (get-owner (uint) (response (optional principal) uint)) + + ;; Authorization helper (response-wrapped read-only) + (is-authorized-or-owner (principal uint) (response bool uint)) ) ) ``` From d9ebc0743a63e8aad65b1c731ab841729e6113d1 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 7 Feb 2026 07:13:56 -0700 Subject: [PATCH 18/28] docs(sip-XXX): update Reputation Registry trait to match v2 contracts Replace the Reputation Registry trait code block with the actual trait definition from reputation-registry-trait-v2.clar. Add design rationale explaining that traits only include public state-changing functions, as read-only functions return raw types that Clarity traits cannot enforce. The updated trait includes only the six public functions: approve-client, give-feedback (3 variants), revoke-feedback, and append-response. All read-only functions (read-feedback, get-summary, read-all-feedback, get-last-index, get-clients, get-approved-limit, get-response-count, get-responders) remain part of the implementation but exist outside the trait definition since they return raw tuples/uints/optionals. Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 43 +++++++----------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index c09e1050..8e8d8a8a 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -476,50 +476,33 @@ This method should be defined as read-only, i.e. `define-read-only`. ### Reputation Registry Trait Implementation +**Design Rationale:** The trait only includes public state-changing functions. Clarity traits can only enforce function signatures that return `(response ...)` types. Read-only functions like `read-feedback`, `get-summary`, `read-all-feedback`, `get-last-index`, `get-clients`, `get-approved-limit`, `get-response-count`, and `get-responders` return raw types (tuples, uints, optionals) that cannot be enforced by Clarity traits. These functions are still part of the implementation contract but exist outside the trait definition. + ```clarity +;; title: reputation-registry-trait +;; version: 2.0.0 +;; summary: Trait definition for ERC-8004 Reputation Registry +;; description: Defines the interface for reputation registry contracts. Includes all public state-changing functions. Read-only functions are not included as they return raw types (tuples, uints, optionals). + (define-trait reputation-registry-trait ( - ;; Pre-authorize a client to provide feedback + ;; Client approval (approve-client (uint principal uint) (response bool uint)) - ;; Submit feedback (permissionless) + ;; Feedback submission (permissionless) (give-feedback (uint int uint (string-utf8 64) (string-utf8 64) (string-utf8 512) (string-utf8 512) (buff 32)) (response uint uint)) - ;; Submit feedback (on-chain approval) + ;; Feedback submission (pre-approved client) (give-feedback-approved (uint int uint (string-utf8 64) (string-utf8 64) (string-utf8 512) (string-utf8 512) (buff 32)) (response uint uint)) - ;; Submit feedback (SIP-018 signature) + ;; Feedback submission (signed authorization) (give-feedback-signed (uint int uint (string-utf8 64) (string-utf8 64) (string-utf8 512) (string-utf8 512) (buff 32) principal uint uint (buff 65)) (response uint uint)) - ;; Revoke submitted feedback + ;; Feedback management (revoke-feedback (uint uint) (response bool uint)) - ;; Respond to feedback + ;; Response management (append-response (uint principal uint (string-utf8 512) (buff 32)) (response bool uint)) - - ;; Read single feedback entry - (read-feedback (uint principal uint) (response (optional {value: int, value-decimals: uint, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}) uint)) - - ;; Get aggregate reputation summary (WAD-normalized) - (get-summary (uint (list 200 principal) (string-utf8 64) (string-utf8 64)) (response {count: uint, summary-value: int, summary-value-decimals: uint} uint)) - - ;; Read filtered feedback - (read-all-feedback (uint (optional (list 50 principal)) (optional (string-utf8 64)) (optional (string-utf8 64)) bool) (response (list 50 {client: principal, index: uint, value: int, value-decimals: uint, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}) uint)) - - ;; Get last feedback index for a client - (get-last-index (uint principal) (response uint uint)) - - ;; Get all clients who gave feedback - (get-clients (uint) (response (optional (list 1024 principal)) uint)) - - ;; Get approved index limit for a client - (get-approved-limit (uint principal) (response uint uint)) - - ;; Get response count with optional filters - (get-response-count (uint (optional principal) (optional uint) (optional (list 200 principal))) (response uint uint)) - - ;; Get responders to feedback - (get-responders (uint principal uint) (response (optional (list 256 principal)) uint)) ) ) ``` From 20be69192f6521b4989c6fc29c5c8734affeafda Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 7 Feb 2026 07:14:30 -0700 Subject: [PATCH 19/28] docs(sip-XXX): update Validation Registry trait to match v2 contracts Replace the Validation Registry trait code block with the actual trait definition from validation-registry-trait-v2.clar. Add design rationale explaining that traits only include public state-changing functions, as read-only functions return raw types that Clarity traits cannot enforce. The updated trait includes only the two public functions: validation-request and validation-response. All read-only functions (get-validation-status, get-summary, get-agent-validations, get-validator-requests, get-identity-registry, get-version) remain part of the implementation but exist outside the trait definition since they return raw tuples/optionals/ principals/strings. Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 29 ++++++++---------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 8e8d8a8a..9d1d2d8b 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -592,32 +592,21 @@ This method should be defined as read-only, i.e. `define-read-only`. ### Validation Registry Trait Implementation +**Design Rationale:** The trait only includes public state-changing functions. Clarity traits can only enforce function signatures that return `(response ...)` types. Read-only functions like `get-validation-status`, `get-summary`, `get-agent-validations`, `get-validator-requests`, `get-identity-registry`, and `get-version` return raw types (tuples, optionals, principals, strings) that cannot be enforced by Clarity traits. These functions are still part of the implementation contract but exist outside the trait definition. + ```clarity +;; title: validation-registry-trait +;; version: 2.0.0 +;; summary: Trait definition for ERC-8004 Validation Registry +;; description: Defines the interface for validation registry contracts. Includes all public state-changing functions. Read-only functions are not included as they return raw types (tuples, optionals). + (define-trait validation-registry-trait ( - ;; Request validation from a validator + ;; Validation request (validation-request (principal uint (string-utf8 512) (buff 32)) (response bool uint)) - ;; Submit validation response (progressive: can be called multiple times) + ;; Validation response (validation-response ((buff 32) uint (string-utf8 512) (buff 32) (string-utf8 64)) (response bool uint)) - - ;; Get validation status (returns none if not found) - (get-validation-status ((buff 32)) (optional {validator: principal, agent-id: uint, response: uint, response-hash: (buff 32), tag: (string-utf8 64), last-update: uint, has-response: bool})) - - ;; Get aggregate validation summary (only counts entries with has-response=true) - (get-summary (uint (optional (list 200 principal)) (optional (string-utf8 64))) {count: uint, avg-response: uint}) - - ;; Get all validations for an agent - (get-agent-validations (uint) (optional (list 1024 (buff 32)))) - - ;; Get all requests for a validator - (get-validator-requests (principal) (optional (list 1024 (buff 32)))) - - ;; Get linked identity registry - (get-identity-registry () principal) - - ;; Get contract version - (get-version () (string-utf8 8)) ) ) ``` From e56d7301e0f89f76a03686ebf7b7381e3df00c5e Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 7 Feb 2026 07:18:42 -0700 Subject: [PATCH 20/28] docs(sip-XXX): update reputation registry read-only functions to match v2 contracts - Add wad-value field to read-feedback return tuple - Update get-summary to O(1) running totals (remove filter params) - Update read-all-feedback with cursor pagination and wad-value - Remove response wrapping from get-last-index and get-approved-limit - Add cursor pagination to get-clients, get-response-count, get-responders - Add new functions: get-agent-feedback-count, get-response-count-single, get-identity-registry, get-auth-message-hash - Update Design Rationale to list all read-only functions All signatures now match reputation-registry.clar v2.0.0. PAGE_SIZE=14 for pagination. Co-Authored-By: Claude Opus 4.6 --- sips/sip-XXX/sip-XXX-agent-registries.md | 66 ++++++++++++++++++------ 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 9d1d2d8b..806807e4 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -412,71 +412,103 @@ This method must be defined with `define-public`. #### Read Feedback -`(read-feedback ((agent-id uint) (client principal) (index uint)) (response (optional {value: int, value-decimals: uint, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}) uint))` +`(read-feedback ((agent-id uint) (client principal) (index uint)) (optional {value: int, value-decimals: uint, wad-value: int, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}))` -Retrieve a specific feedback entry. Returns none if the feedback does not exist. +Retrieve a specific feedback entry. Returns none if the feedback does not exist. The wad-value field contains the WAD-normalized (18 decimals) value for O(1) aggregation. This method should be defined as read-only, i.e. `define-read-only`. #### Get Summary -`(get-summary ((agent-id uint) (client-addresses (list 200 principal)) (tag1 (string-utf8 64)) (tag2 (string-utf8 64))) (response {count: uint, summary-value: int, summary-value-decimals: uint} uint))` +`(get-summary ((agent-id uint)) {count: uint, summary-value: int, summary-value-decimals: uint})` -Calculate aggregate reputation metrics for an agent across specified clients and optional tag filters. All feedback values are normalized to WAD (18 decimals) for averaging, then scaled back to the mode (most common) decimals value among the feedback entries. Empty tag strings match all values. Returns empty summary if client-addresses is empty or no matching feedback exists. +Returns O(1) aggregate metrics using running totals. All feedback values are normalized to WAD (18 decimals) for averaging. The summary-value is the average WAD-normalized value, and summary-value-decimals is always u18. Returns {count: u0, summary-value: 0, summary-value-decimals: u18} if no feedback exists. For filtered aggregations (by client/tag), use SIP-019 indexer. This method should be defined as read-only, i.e. `define-read-only`. #### Read All Feedback -`(read-all-feedback ((agent-id uint) (opt-clients (optional (list 50 principal))) (opt-tag1 (optional (string-utf8 64))) (opt-tag2 (optional (string-utf8 64))) (include-revoked bool)) (response (list 50 {client: principal, index: uint, value: int, value-decimals: uint, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}) uint))` +`(read-all-feedback ((agent-id uint) (opt-tag1 (optional (string-utf8 64))) (opt-tag2 (optional (string-utf8 64))) (include-revoked bool) (opt-cursor (optional uint))) {items: (list 14 {client: principal, index: uint, value: int, value-decimals: uint, wad-value: int, tag1: (string-utf8 64), tag2: (string-utf8 64), is-revoked: bool}), cursor: (optional uint)})` -Retrieve feedback entries for an agent with optional filters. If opt-clients is none, reads from all clients who have given feedback. Tag filters are optional. Maximum 50 entries returned. +Retrieve feedback entries for an agent using cursor-based pagination (PAGE_SIZE=14). Uses global feedback sequence for cross-client iteration. Tag filters are optional (none matches all). Returns {items: (list 14 {...}), cursor: (optional uint)} where cursor is some(offset) if more results exist. Items include wad-value field for WAD-normalized values. This method should be defined as read-only, i.e. `define-read-only`. #### Get Last Index -`(get-last-index ((agent-id uint) (client principal)) (response uint uint))` +`(get-last-index ((agent-id uint) (client principal)) uint)` -Get the last feedback index submitted by a client for an agent. Returns 0 if no feedback has been submitted. +Get the last feedback index submitted by a client for an agent. Returns u0 if no feedback has been submitted. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Agent Feedback Count + +`(get-agent-feedback-count ((agent-id uint)) uint)` + +Get the total number of feedback entries for an agent across all clients. Returns the last global index, or u0 if no feedback exists. This is the global feedback sequence counter. This method should be defined as read-only, i.e. `define-read-only`. #### Get Clients -`(get-clients ((agent-id uint)) (response (optional (list 1024 principal)) uint))` +`(get-clients ((agent-id uint) (opt-cursor (optional uint))) {clients: (list 14 principal), cursor: (optional uint)})` -Get the list of all clients who have given feedback for an agent. +Get clients who have given feedback for an agent using cursor-based pagination (PAGE_SIZE=14). Returns {clients: (list 14 principal), cursor: (optional uint)} where cursor is some(offset) if more results exist. This method should be defined as read-only, i.e. `define-read-only`. #### Get Approved Limit -`(get-approved-limit ((agent-id uint) (client principal)) (response uint uint))` +`(get-approved-limit ((agent-id uint) (client principal)) uint)` -Get the approved index limit for a client. Returns 0 if no approval exists. +Get the approved index limit for a client. Returns u0 if no approval exists. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Response Count Single + +`(get-response-count-single ((agent-id uint) (client principal) (index uint) (responder principal)) uint)` + +Get the response count for a specific responder on a specific feedback entry. Returns u0 if no responses exist. This is a legacy function kept for backwards compatibility; prefer get-response-count for flexible querying. This method should be defined as read-only, i.e. `define-read-only`. #### Get Response Count -`(get-response-count ((agent-id uint) (opt-client (optional principal)) (opt-feedback-index (optional uint)) (opt-responders (optional (list 200 principal)))) (response uint uint))` +`(get-response-count ((agent-id uint) (opt-client (optional principal)) (opt-feedback-index (optional uint)) (opt-responders (optional (list 200 principal))) (opt-cursor (optional uint))) {total: uint, cursor: (optional uint)})` -Flexible response counting with optional filters. Can count responses across all clients, a specific client, a specific feedback entry, or specific responders. If opt-client is none, counts across all clients. If opt-feedback-index is none or 0, counts all feedback for the client(s). If opt-responders is provided, only counts responses from those principals. +Flexible response counting with optional filters and cursor-based pagination. Can count responses across all clients, a specific client, a specific feedback entry, or specific responders. If opt-client is none, counts across all clients. If opt-feedback-index is none or 0, counts all feedback for the client(s). If opt-responders is provided, only counts responses from those principals. Returns {total: uint, cursor: (optional uint)} where cursor is some(offset) if more results exist (when paginating across clients/feedback). This method should be defined as read-only, i.e. `define-read-only`. #### Get Responders -`(get-responders ((agent-id uint) (client principal) (index uint)) (response (optional (list 256 principal)) uint))` +`(get-responders ((agent-id uint) (client principal) (index uint) (opt-cursor (optional uint))) {responders: (list 14 principal), cursor: (optional uint)})` + +Get principals who have responded to a specific feedback entry using cursor-based pagination (PAGE_SIZE=14). Returns {responders: (list 14 principal), cursor: (optional uint)} where cursor is some(offset) if more results exist. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Identity Registry + +`(get-identity-registry () principal)` + +Return the principal of the linked Identity Registry contract. This is hardcoded to .identity-registry at deployment time. + +This method should be defined as read-only, i.e. `define-read-only`. + +#### Get Auth Message Hash + +`(get-auth-message-hash ((agent-id uint) (client principal) (index-limit uint) (expiry uint) (signer principal)) (buff 32))` -Get the list of principals who have responded to a specific feedback entry. +Calculate the SIP-018 message hash for give-feedback-signed authorization. Exposed for off-chain tooling to generate signatures. Returns the 32-byte hash that must be signed by the signer principal. This method should be defined as read-only, i.e. `define-read-only`. ### Reputation Registry Trait Implementation -**Design Rationale:** The trait only includes public state-changing functions. Clarity traits can only enforce function signatures that return `(response ...)` types. Read-only functions like `read-feedback`, `get-summary`, `read-all-feedback`, `get-last-index`, `get-clients`, `get-approved-limit`, `get-response-count`, and `get-responders` return raw types (tuples, uints, optionals) that cannot be enforced by Clarity traits. These functions are still part of the implementation contract but exist outside the trait definition. +**Design Rationale:** The trait only includes public state-changing functions. Clarity traits can only enforce function signatures that return `(response ...)` types. Read-only functions like `read-feedback`, `get-summary`, `read-all-feedback`, `get-last-index`, `get-agent-feedback-count`, `get-clients`, `get-approved-limit`, `get-response-count-single`, `get-response-count`, `get-responders`, `get-identity-registry`, and `get-auth-message-hash` return raw types (tuples, uints, optionals, buffs, principals) that cannot be enforced by Clarity traits. These functions are still part of the implementation contract but exist outside the trait definition. ```clarity ;; title: reputation-registry-trait From 7cdf4424efe0a6ae6b24a7d3ed98d377ea2b5f44 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 7 Feb 2026 07:19:24 -0700 Subject: [PATCH 21/28] docs(sip-XXX): update validation registry read-only functions to match v2 contracts - Update get-summary to O(1) running totals (remove filter params) - Add cursor pagination to get-agent-validations and get-validator-requests - All signatures now match validation-registry.clar v2.0.0 - PAGE_SIZE=14 for pagination Co-Authored-By: Claude Opus 4.6 --- sips/sip-XXX/sip-XXX-agent-registries.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 806807e4..b2832e09 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -584,25 +584,25 @@ This method should be defined as read-only, i.e. `define-read-only`. #### Get Summary -`(get-summary ((agent-id uint) (opt-validators (optional (list 200 principal))) (opt-tag (optional (string-utf8 64)))) {count: uint, avg-response: uint})` +`(get-summary ((agent-id uint)) {count: uint, avg-response: uint})` -Calculate aggregate validation metrics for an agent, optionally filtered by validators and a single tag. The opt-tag parameter accepts a single optional string instead of a list - an empty string or none matches all tags. Only validations where has-response is true are counted in the aggregation. Returns a tuple with count (number of matching validations) and avg-response (average validation score), or {count: 0, avg-response: 0} if no matching validations exist. +Returns O(1) aggregate metrics using running totals. Only validations where has-response is true are counted. Returns {count: uint, avg-response: uint} where avg-response is the average validation score, or {count: u0, avg-response: u0} if no validations exist. For filtered aggregations (by validator/tag), use SIP-019 indexer. This method should be defined as read-only, i.e. `define-read-only`. #### Get Agent Validations -`(get-agent-validations ((agent-id uint)) (optional (list 1024 (buff 32))))` +`(get-agent-validations ((agent-id uint) (opt-cursor (optional uint))) {validations: (list 14 (buff 32)), cursor: (optional uint)})` -Get all validation request hashes for an agent. Returns none if the agent has no validations. +Get validation request hashes for an agent using cursor-based pagination (PAGE_SIZE=14). Returns {validations: (list 14 (buff 32)), cursor: (optional uint)} where cursor is some(offset) if more results exist. This method should be defined as read-only, i.e. `define-read-only`. #### Get Validator Requests -`(get-validator-requests ((validator principal)) (optional (list 1024 (buff 32))))` +`(get-validator-requests ((validator principal) (opt-cursor (optional uint))) {requests: (list 14 (buff 32)), cursor: (optional uint)})` -Get all validation request hashes assigned to a validator. Returns none if the validator has no requests. +Get validation request hashes assigned to a validator using cursor-based pagination (PAGE_SIZE=14). Returns {requests: (list 14 (buff 32)), cursor: (optional uint)} where cursor is some(offset) if more results exist. This method should be defined as read-only, i.e. `define-read-only`. From a4853d86fd056342defc26403618a0d8d768445a Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 7 Feb 2026 07:21:46 -0700 Subject: [PATCH 22/28] docs(sip-XXX): update testnet addresses to v2 contracts Update testnet deployment addresses to use -v2 suffix matching the actual deployed contract names (identity-registry-v2, reputation-registry-v2, validation-registry-v2). Co-Authored-By: Claude --- sips/sip-XXX/sip-XXX-agent-registries.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index b2832e09..4e139f7c 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -1321,9 +1321,9 @@ Reference implementations are available at: The following contracts are deployed on Stacks testnet: -- Identity Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.identity-registry` -- Reputation Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.reputation-registry` -- Validation Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.validation-registry` +- Identity Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.identity-registry-v2` +- Reputation Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.reputation-registry-v2` +- Validation Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.validation-registry-v2` ## Mainnet Deployments From aa83306b28c1d1277cb7ce67b08c42bf0a23b00e Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Wed, 11 Feb 2026 16:13:31 -0700 Subject: [PATCH 23/28] docs(sip-XXX): add mainnet deployment addresses and update CAIP-2 examples Add mainnet contract addresses at SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD for all three traits and three registry contracts. Update CAIP-2 multichain identity examples to use real deployed addresses. Expand testnet section to include trait contracts alongside registry contracts. Co-Authored-By: Claude Opus 4.6 --- sips/sip-XXX/sip-XXX-agent-registries.md | 34 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index 4e139f7c..d60dcde6 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -712,8 +712,8 @@ For Stacks agents: | Network | Chain ID | Example | | ------- | -------- | ------- | -| Stacks Mainnet | 1 | `stacks:1:SP000000000000000000002Q6VF78.identity-registry:42` | -| Stacks Testnet | 2147483648 | `stacks:2147483648:ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.identity-registry:0` | +| Stacks Mainnet | 1 | `stacks:1:SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.identity-registry-v2:42` | +| Stacks Testnet | 2147483648 | `stacks:2147483648:ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.identity-registry-v2:0` | ## Cross-Chain Registration @@ -722,7 +722,7 @@ Agents can be registered on multiple blockchains and advertise all registrations ```json { "agentId": 42, - "agentRegistry": "stacks:1:SP000000000000000000002Q6VF78.identity-registry" + "agentRegistry": "stacks:1:SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.identity-registry-v2" } ``` @@ -735,7 +735,7 @@ An agent registered on both Stacks mainnet (agent ID 42) and Ethereum mainnet (a "registrations": [ { "agentId": 42, - "agentRegistry": "stacks:1:SP000000000000000000002Q6VF78.identity-registry" + "agentRegistry": "stacks:1:SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.identity-registry-v2" }, { "agentId": 123, @@ -804,7 +804,7 @@ The registration file MUST follow the ERC-8004 [3] registration file structure t "registrations": [ { "agentId": 42, - "agentRegistry": "stacks:1:SP000000000000000000002Q6VF78.identity-registry" + "agentRegistry": "stacks:1:SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.identity-registry-v2" } ], "supportedTrust": [ @@ -1319,7 +1319,15 @@ Reference implementations are available at: ## Testnet Deployments -The following contracts are deployed on Stacks testnet: +The following contracts are deployed on Stacks testnet at `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18`: + +**Traits:** + +- Identity Registry Trait: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.identity-registry-trait-v2` +- Reputation Registry Trait: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.reputation-registry-trait-v2` +- Validation Registry Trait: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.validation-registry-trait-v2` + +**Contracts:** - Identity Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.identity-registry-v2` - Reputation Registry: `ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.reputation-registry-v2` @@ -1327,7 +1335,19 @@ The following contracts are deployed on Stacks testnet: ## Mainnet Deployments -Mainnet deployment addresses will be added upon activation. +The following contracts are deployed on Stacks mainnet at `SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD`: + +**Traits:** + +- Identity Registry Trait: `SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.identity-registry-trait-v2` +- Reputation Registry Trait: `SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.reputation-registry-trait-v2` +- Validation Registry Trait: `SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.validation-registry-trait-v2` + +**Contracts:** + +- Identity Registry: `SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.identity-registry-v2` +- Reputation Registry: `SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.reputation-registry-v2` +- Validation Registry: `SP1NMR7MY0TJ1QA7WQBZ6504KC79PZNTRQH4YGFJD.validation-registry-v2` # References From 8e6fc95c1f96d3cba7e1f2498c79bde4c3c7529d Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Wed, 11 Feb 2026 17:07:22 -0700 Subject: [PATCH 24/28] Accept SIP-041 Co-authored-by: wileyj <2847772+wileyj@users.noreply.github.com> --- sips/sip-XXX/sip-XXX-agent-registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-XXX/sip-XXX-agent-registries.md index d60dcde6..55eef101 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-XXX/sip-XXX-agent-registries.md @@ -1,6 +1,6 @@ # Preamble -SIP Number: XXX +SIP Number: 041 Title: Standard Trait Definition for Agent Registries From c636cdafead86162771d417da19e52629204d4d8 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Wed, 11 Feb 2026 17:10:56 -0700 Subject: [PATCH 25/28] docs(sip-041): rename from sip-XXX to sip-041 and add PR link Rename directory and file from sip-XXX to sip-041 to match assigned SIP number. Update Discussions-To with PR stacksgov/sips#258. Co-Authored-By: Claude Opus 4.6 --- .../sip-041-agent-registries.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename sips/{sip-XXX/sip-XXX-agent-registries.md => sip-041/sip-041-agent-registries.md} (99%) diff --git a/sips/sip-XXX/sip-XXX-agent-registries.md b/sips/sip-041/sip-041-agent-registries.md similarity index 99% rename from sips/sip-XXX/sip-XXX-agent-registries.md rename to sips/sip-041/sip-041-agent-registries.md index 55eef101..863df0b7 100644 --- a/sips/sip-XXX/sip-XXX-agent-registries.md +++ b/sips/sip-041/sip-041-agent-registries.md @@ -24,7 +24,7 @@ Sign-offs: Layer: Traits Discussions-To: -- SIP pull request: https://github.com/stacksgov/sips/pull/XXX +- SIP pull request: https://github.com/stacksgov/sips/pull/258 # Abstract From c841dec1fed68aa6d28f0f30d280cbed28676dd9 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Wed, 11 Feb 2026 17:12:16 -0700 Subject: [PATCH 26/28] chore: remove .gitignore and CLAUDE.md changes from PR Keep PR scoped to only the SIP-041 document. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 - CLAUDE.md | 58 ------------------------------------------------------ 2 files changed, 59 deletions(-) delete mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index 22bef3cc..19d57931 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ *.rej .aider* -.planning/ diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 2014482d..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,58 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Repository Overview - -This is the Stacks Improvement Proposals (SIPs) repository - a governance documentation repository for the Stacks blockchain. It contains proposals for changes to the blockchain's design, implementation, operation, and governance. - -**This is NOT a code repository.** It is a collection of markdown documents following a formal proposal and ratification process. - -## Repository Structure - -- `sips/` - Contains all SIP documents organized by number (e.g., `sip-000/`, `sip-001/`) -- `considerations/` - Contains Consideration Advisory Board (CAB) information and meeting minutes -- `sips/SIP_TEMPLATE.md` - Template for new SIP submissions - -## SIP Document Format - -All SIPs are markdown files with required sections in order: -1. **Preamble** - Metadata fields (SIP Number, Title, Author, Consideration, Type, Status, Created, License, Sign-off) -2. **Abstract** - High-level summary (max 5000 words) -3. **Copyright** - License information -4. **Introduction** - Problem description and proposed solution -5. **Specification** - Detailed technical specification -6. **Related Work** - Alternative solutions and bibliography -7. **Backwards Compatibility** - Breaking changes and mitigations -8. **Activation** - Timeline, criteria, and process for activation -9. **Reference Implementation** - Links to implementations - -## SIP Types - -- **Consensus** - Requires all implementations to adopt (hard/soft fork) -- **Standard** - Affects implementations but not consensus -- **Operation** - Concerns node operators and miners -- **Meta** - Changes to the SIP process itself -- **Informational** - Provides information without requiring action - -## SIP Considerations (Review Tracks) - -- **Technical** - Technical expertise review -- **Economic** - Token economics, fundraising, grants -- **Governance** - SIP process and committee structure -- **Ethics** - Behavioral standards for office-holders -- **Diversity** - User growth and outreach - -## SIP Workflow - -Draft → Accepted → Recommended → Activation-In-Progress → Ratified - -SIPs can also be: Rejected, Obsolete, Replaced, or Withdrawn - -## Working with SIPs - -When creating or editing SIPs: -- Follow the template in `sips/SIP_TEMPLATE.md` -- Place SIP files in `sips/sip-XXX/sip-XXX-title.md` -- Supplementary materials go in the same directory as `SIP-XXXX-YYY.ext` -- Use approved licenses: BSD-2-Clause, BSD-3-Clause, CC0-1.0, GNU-All-Permissive, GPL-2.0+, LGPL-2.1+ From be02aff03ae498146ca69c473e4f4d8ae596d7a3 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Wed, 11 Feb 2026 19:47:18 -0700 Subject: [PATCH 27/28] docs(sip-041): fix tx-sender vs contract-caller documentation Correct inaccurate claims about how tx-sender and contract-caller behave across contract call boundaries, especially with as-contract. Add sender context behavior table, reframe rationale around composability + security, and fix misleading attack scenario. Co-Authored-By: Claude Opus 4.6 --- sips/sip-041/sip-041-agent-registries.md | 63 ++++++++++++++++++------ 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/sips/sip-041/sip-041-agent-registries.md b/sips/sip-041/sip-041-agent-registries.md index 863df0b7..b8a6feb2 100644 --- a/sips/sip-041/sip-041-agent-registries.md +++ b/sips/sip-041/sip-041-agent-registries.md @@ -894,11 +894,31 @@ The v2.0.0 contracts implement a strict authorization model based on transaction ### tx-sender vs contract-caller -All ownership and authorization checks in v2.0.0 use `tx-sender` exclusively, never `contract-caller`. This is a critical security decision that prevents delegation attacks. +All ownership and authorization checks in v2.0.0 use `tx-sender` exclusively, never `contract-caller`. This design provides both composability and security by leveraging how Clarity's sender context propagates across contract call boundaries. + +**How sender context works in Clarity:** + +In Clarity, `tx-sender` and `contract-caller` behave differently as execution crosses contract boundaries: + +| Call pattern | `tx-sender` at callee | `contract-caller` at callee | +|---|---|---| +| User calls A directly | User | User | +| User calls B, B calls A via `contract-call?` | User | B | +| User calls B, B calls A via `as-contract` | B | B | +| User calls C, C calls B, B calls A (chain) | User | B | +| User calls C, C uses `as-contract` to call B, B calls A | C | B | + +Key observations: +- **`tx-sender`** remains the original transaction sender through normal `contract-call?` chains. It only changes when a contract explicitly uses `as-contract`, which resets both `tx-sender` and `contract-caller` to the calling contract's principal. +- **`contract-caller`** always reflects the immediate caller, changing at every contract boundary regardless of whether `as-contract` is used. **Why tx-sender?** -Using `tx-sender` ensures that only the actual transaction originator can perform privileged operations. If contracts used `contract-caller` instead, a malicious intermediary contract could impersonate the owner by calling registry functions on behalf of users, even without explicit permission. +Using `tx-sender` for authorization provides two benefits: + +1. **Composability**: Because `tx-sender` passes through normal `contract-call?` chains unchanged, intermediary helper contracts can exist between the user and the registry without breaking authorization. If the registries checked `contract-caller` instead, every intermediary contract would need to be explicitly approved as an operator, preventing composable contract architectures. + +2. **Security**: A contract cannot change `tx-sender` to another user's principal—it can only change `tx-sender` to its own principal via `as-contract`. This means a malicious intermediary contract cannot impersonate a user; it can only act as itself. **Example from identity-registry.clar:** @@ -912,7 +932,7 @@ Using `tx-sender` ensures that only the actual transaction originator can perfor ) ``` -The `is-authorized` helper checks if `tx-sender` (not `contract-caller`) is either the NFT owner or an approved operator: +The `is-authorized` helper checks if `tx-sender` is either the NFT owner or an approved operator: ```clarity (define-private (is-authorized (agent-id uint) (caller principal)) @@ -930,7 +950,9 @@ The `is-authorized` helper checks if `tx-sender` (not `contract-caller`) is eith ) ``` -**Trade-off:** This approach prevents delegation through intermediary contracts. For delegated operations, owners must use `set-approval-for-all` to explicitly grant operator permissions instead of relying on contract-level delegation. +Because this checks `tx-sender`, a user can call the registry through any number of intermediary contracts via `contract-call?` and authorization still succeeds. If an intermediary contract uses `as-contract`, `tx-sender` changes to that contract's principal—which would fail the authorization check unless that contract is an approved operator. + +**Trade-off:** Contracts that need to act on behalf of an agent owner cannot use `as-contract` to relay calls, since that changes `tx-sender`. Instead, owners must use `set-approval-for-all` to explicitly grant operator permissions to contracts that need to manage agents on their behalf. ### is-authorized-or-owner Pattern @@ -1198,33 +1220,46 @@ The v2.0.0 `validation-response` function allows validators to submit multiple u ) ``` -## tx-sender Authorization and Delegation +## tx-sender Authorization and Composability + +The exclusive use of `tx-sender` for authorization checks provides both composability and security, but requires understanding how Clarity's sender context changes across contract boundaries. + +**Composability Benefit:** -The exclusive use of `tx-sender` for authorization checks provides strong security guarantees but limits delegation patterns. +Because `tx-sender` remains unchanged through normal `contract-call?` chains, users can interact with agent registries through intermediary contracts (wrappers, routers, batch operations) without breaking authorization. If the registries checked `contract-caller` instead, each intermediary contract would become the caller and would need to be explicitly approved as an operator—preventing composable contract architectures. **Security Benefit:** -By checking `tx-sender` instead of `contract-caller`, the registries prevent intermediary contract attacks. A malicious contract cannot impersonate the owner by forwarding calls, because `tx-sender` always represents the transaction originator, not the immediate caller. +A contract cannot set `tx-sender` to an arbitrary principal. The only way a contract can change `tx-sender` is via `as-contract`, which sets it to the contract's own principal. This means: +- A malicious intermediary contract cannot impersonate a user—it can only act as itself +- If a malicious contract uses `as-contract` to call the registry, `tx-sender` becomes the malicious contract's principal, which is not the agent owner and fails authorization **Example Attack (prevented by tx-sender):** -If contracts used `contract-caller`: 1. Alice owns agent #42 2. Bob deploys a malicious contract that calls `set-agent-uri(42, "evil-uri")` 3. Bob tricks Alice into calling his contract for an unrelated purpose -4. The malicious contract's call to `set-agent-uri` would have `contract-caller = malicious-contract` -5. If authorization checked `contract-caller`, the call would fail (good) -6. But if the malicious contract first called another intermediary that then called the registry, `contract-caller` could be spoofed +4. The malicious contract calls `set-agent-uri` — but `tx-sender` is still Alice (the original transaction sender), not the malicious contract +5. However, Alice IS the owner, so this call would succeed if the malicious contract uses `contract-call?` + +This illustrates an important nuance: `tx-sender` authorization does not prevent a malicious contract from performing authorized actions within a transaction that Alice initiates. The protection is that Alice must sign the transaction—a malicious contract cannot forge `tx-sender` to be Alice's principal from Bob's transaction. If Bob calls the malicious contract, `tx-sender` is Bob, not Alice. + +If the registries used `contract-caller` instead, they would check the immediate caller (the malicious contract's principal), which would fail. However, `contract-caller` has its own trade-off: legitimate intermediary contracts would also fail, requiring each helper contract to be pre-approved as an operator. This makes `contract-caller` less composable—it restricts to specific direct callers only. + +**as-contract Boundary:** -Using `tx-sender` prevents this entire class of attacks because `tx-sender` always equals Alice regardless of call chain depth. +When a contract uses `as-contract` to call the registry, both `tx-sender` and `contract-caller` change to that contract's principal. This means: +- The contract acts on its own behalf, not the user's +- The call only succeeds if the contract itself is an approved operator for the agent +- This is the correct behavior: `as-contract` is an explicit opt-in to change sender context **Trade-off:** -The downside is that delegation through smart contracts is not supported. For example, a DAO contract cannot directly manage agents on behalf of members. However, the `set-approval-for-all` function provides an alternative: owners can explicitly grant operator permissions to contracts, enabling controlled delegation. +Contracts that need to manage agents on behalf of owners cannot use `as-contract` to relay calls, since that changes `tx-sender` to the contract's principal. Instead, owners must use `set-approval-for-all` to explicitly grant operator permissions to contracts that need to act on their behalf. **Recommendation:** -For DAO or multisig agent management, use `set-approval-for-all` to grant the DAO/multisig contract operator privileges. This provides explicit, revocable delegation without compromising the tx-sender security model. +For DAO or multisig agent management, use `set-approval-for-all` to grant the DAO/multisig contract operator privileges. This provides explicit, revocable delegation while preserving composability for normal `contract-call?` chains. ## Agent Wallet Security From c6c718ff0a0bd90a429ce433ad4a64d6f0c2ac4f Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Wed, 11 Feb 2026 19:55:18 -0700 Subject: [PATCH 28/28] docs(sip-041): trim redundant content for conciseness Remove ~425 lines of duplicate and verbose content: - Remove Error Code Summary section (duplicates per-function tables) - Consolidate Clarity trait design rationale into single paragraph - Condense Authorization Model from ~200 lines to ~25 lines - Remove Agent Wallet System subsection (duplicates Specification) - Trim Security Considerations (remove code examples and verbose scenarios) - Remove duplicate tx-sender security section Co-Authored-By: Claude Opus 4.6 --- sips/sip-041/sip-041-agent-registries.md | 451 +---------------------- 1 file changed, 13 insertions(+), 438 deletions(-) diff --git a/sips/sip-041/sip-041-agent-registries.md b/sips/sip-041/sip-041-agent-registries.md index b8a6feb2..45eb2885 100644 --- a/sips/sip-041/sip-041-agent-registries.md +++ b/sips/sip-041/sip-041-agent-registries.md @@ -54,6 +54,8 @@ This standard is designed to be compatible with ERC-8004, enabling cross-chain a The agent registry standard consists of three separate traits, each addressing a distinct aspect of agent management. Implementations may deploy these as separate contracts or combine them as appropriate. +**Trait Design Note:** Clarity traits can only enforce function signatures that return `(response ...)` types. Each trait below includes only public state-changing functions and response-wrapped read-only functions. Read-only functions returning raw types (optionals, tuples, bools, strings) cannot be enforced by traits but are still required in implementation contracts and are documented in the function listings. + ## Identity Registry Trait The Identity Registry manages agent registration, ownership, and metadata. It implements the SIP-009 NFT trait, providing standard wallet and explorer compatibility while maintaining sequential agent ID assignment and cross-contract authorization checks. @@ -276,8 +278,6 @@ This method should be defined as read-only, i.e. `define-read-only`. ### Identity Registry Trait Implementation -**Design Rationale:** The trait only includes public state-changing functions and response-wrapped read-only functions (SIP-009 trait functions + `is-authorized-or-owner`). Clarity traits can only enforce function signatures that return `(response ...)` types. Read-only functions that return raw types like `(optional principal)`, `bool`, or `(string-utf8 8)` (`owner-of`, `get-uri`, `get-metadata`, `is-approved-for-all`, `get-agent-wallet`, `get-version`) are not included in the trait because Clarity cannot enforce their signatures via the trait system. These functions are still part of the implementation contract but exist outside the trait definition. - ```clarity ;; title: identity-registry-trait ;; version: 2.0.0 @@ -508,8 +508,6 @@ This method should be defined as read-only, i.e. `define-read-only`. ### Reputation Registry Trait Implementation -**Design Rationale:** The trait only includes public state-changing functions. Clarity traits can only enforce function signatures that return `(response ...)` types. Read-only functions like `read-feedback`, `get-summary`, `read-all-feedback`, `get-last-index`, `get-agent-feedback-count`, `get-clients`, `get-approved-limit`, `get-response-count-single`, `get-response-count`, `get-responders`, `get-identity-registry`, and `get-auth-message-hash` return raw types (tuples, uints, optionals, buffs, principals) that cannot be enforced by Clarity traits. These functions are still part of the implementation contract but exist outside the trait definition. - ```clarity ;; title: reputation-registry-trait ;; version: 2.0.0 @@ -624,8 +622,6 @@ This method should be defined as read-only, i.e. `define-read-only`. ### Validation Registry Trait Implementation -**Design Rationale:** The trait only includes public state-changing functions. Clarity traits can only enforce function signatures that return `(response ...)` types. Read-only functions like `get-validation-status`, `get-summary`, `get-agent-validations`, `get-validator-requests`, `get-identity-registry`, and `get-version` return raw types (tuples, optionals, principals, strings) that cannot be enforced by Clarity traits. These functions are still part of the implementation contract but exist outside the trait definition. - ```clarity ;; title: validation-registry-trait ;; version: 2.0.0 @@ -643,53 +639,6 @@ This method should be defined as read-only, i.e. `define-read-only`. ) ``` -## Error Code Summary - -This section lists all error codes used across the three registry contracts in v2.0.0. - -### Identity Registry Errors (u1000-u1008) - -| error code | reason | -| ---------- | ------ | -| u1000 | Not authorized (caller is not owner or approved operator) | -| u1001 | Agent not found | -| u1002 | Agent already exists | -| u1003 | Metadata set failed | -| u1004 | Reserved key (agentWallet cannot be set via set-metadata or during registration) | -| u1005 | Invalid sender (tx-sender must match sender parameter in transfer) | -| u1006 | Wallet already set (tx-sender is already the agent wallet) | -| u1007 | Expired signature (deadline passed or exceeds MAX_DEADLINE_DELAY) | -| u1008 | Invalid signature (recovery failed or doesn't match expected principal) | - -### Validation Registry Errors (u2000-u2005) - -| error code | reason | -| ---------- | ------ | -| u2000 | Not authorized (caller is not owner, approved operator, or designated validator) | -| u2001 | Agent not found | -| u2002 | Validation not found | -| u2003 | Validation already exists | -| u2004 | Invalid validator (cannot be self) | -| u2005 | Invalid response (exceeds 100) | - -### Reputation Registry Errors (u3000-u3012) - -| error code | reason | -| ---------- | ------ | -| u3000 | Not authorized (signer is not owner or approved operator) | -| u3001 | Agent not found | -| u3002 | Feedback not found | -| u3003 | Feedback already revoked | -| u3004 | Invalid value (reserved, unused in v2.0.0) | -| u3005 | Self-feedback not allowed (caller is owner or approved operator) | -| u3006 | Invalid index (must be > 0) | -| u3007 | Signature verification failed | -| u3008 | Authorization expired | -| u3009 | Index limit exceeded | -| u3010 | Empty response URI | -| u3011 | Invalid decimals (exceeds 18) | -| u3012 | Empty client list (reserved, unused in v2.0.0) | - # Multichain Identity This standard supports cross-chain agent identity using CAIP-2 [4] compliant identifiers, enabling agents registered on Stacks to be referenced from other chains and vice versa. Agents advertise all their cross-chain registrations in the registration file's `registrations` array. @@ -749,7 +698,7 @@ This enables applications on any chain to discover the agent's presence on other # Implementing in Wallets and Applications -Developers building applications that interact with agent registries should follow these guidelines: +Developers building applications that interact with agent registries should follow these guidelines. ## Validating Registry Contracts @@ -890,216 +839,13 @@ Wallets implementing SIP-018 signing for agent registries should: ## Authorization Model -The v2.0.0 contracts implement a strict authorization model based on transaction sender identity and cross-contract authorization checks. This section documents the key patterns used throughout the registry implementations. - -### tx-sender vs contract-caller - -All ownership and authorization checks in v2.0.0 use `tx-sender` exclusively, never `contract-caller`. This design provides both composability and security by leveraging how Clarity's sender context propagates across contract call boundaries. - -**How sender context works in Clarity:** - -In Clarity, `tx-sender` and `contract-caller` behave differently as execution crosses contract boundaries: - -| Call pattern | `tx-sender` at callee | `contract-caller` at callee | -|---|---|---| -| User calls A directly | User | User | -| User calls B, B calls A via `contract-call?` | User | B | -| User calls B, B calls A via `as-contract` | B | B | -| User calls C, C calls B, B calls A (chain) | User | B | -| User calls C, C uses `as-contract` to call B, B calls A | C | B | - -Key observations: -- **`tx-sender`** remains the original transaction sender through normal `contract-call?` chains. It only changes when a contract explicitly uses `as-contract`, which resets both `tx-sender` and `contract-caller` to the calling contract's principal. -- **`contract-caller`** always reflects the immediate caller, changing at every contract boundary regardless of whether `as-contract` is used. - -**Why tx-sender?** - -Using `tx-sender` for authorization provides two benefits: - -1. **Composability**: Because `tx-sender` passes through normal `contract-call?` chains unchanged, intermediary helper contracts can exist between the user and the registry without breaking authorization. If the registries checked `contract-caller` instead, every intermediary contract would need to be explicitly approved as an operator, preventing composable contract architectures. - -2. **Security**: A contract cannot change `tx-sender` to another user's principal—it can only change `tx-sender` to its own principal via `as-contract`. This means a malicious intermediary contract cannot impersonate a user; it can only act as itself. - -**Example from identity-registry.clar:** - -```clarity -(define-public (set-agent-uri (agent-id uint) (new-uri (string-utf8 512))) - (begin - (asserts! (is-authorized agent-id tx-sender) ERR_NOT_AUTHORIZED) - (map-set uris {agent-id: agent-id} new-uri) - ;; ... - ) -) -``` - -The `is-authorized` helper checks if `tx-sender` is either the NFT owner or an approved operator: - -```clarity -(define-private (is-authorized (agent-id uint) (caller principal)) - (let ( - (owner-opt (nft-get-owner? agent-identity agent-id)) - ) - (match owner-opt owner - (or - (is-eq caller owner) - (is-approved-for-all agent-id caller) - ) - false - ) - ) -) -``` - -Because this checks `tx-sender`, a user can call the registry through any number of intermediary contracts via `contract-call?` and authorization still succeeds. If an intermediary contract uses `as-contract`, `tx-sender` changes to that contract's principal—which would fail the authorization check unless that contract is an approved operator. - -**Trade-off:** Contracts that need to act on behalf of an agent owner cannot use `as-contract` to relay calls, since that changes `tx-sender`. Instead, owners must use `set-approval-for-all` to explicitly grant operator permissions to contracts that need to manage agents on their behalf. - -### is-authorized-or-owner Pattern - -The Identity Registry exposes a public read-only function for cross-contract authorization checks: - -```clarity -(define-read-only (is-authorized-or-owner (spender principal) (agent-id uint)) - (let ( - (owner (unwrap! (nft-get-owner? agent-identity agent-id) ERR_AGENT_NOT_FOUND)) - ) - (ok (or - (is-eq spender owner) - (is-approved-for-all agent-id spender) - )) - ) -) -``` - -This function returns `(response bool uint)`: -- `(ok true)` if the spender is authorized (owner or approved operator) -- `(ok false)` if the spender is not authorized -- `(err u1001)` if the agent does not exist - -**Usage in reputation-registry.clar:** - -The Reputation Registry uses this function to prevent self-feedback. In the permissionless `give-feedback` function: - -```clarity -(let ( - (auth-check (contract-call? .identity-registry is-authorized-or-owner caller agent-id)) -) - ;; Verify agent exists (is-authorized-or-owner returns error if not) - (asserts! (is-ok auth-check) ERR_AGENT_NOT_FOUND) - ;; Verify caller is NOT authorized (prevent self-feedback) - (asserts! (not (unwrap-panic auth-check)) ERR_SELF_FEEDBACK) - ;; ... continue with feedback submission -) -``` - -This pattern enables composable authorization across registry contracts while maintaining the tx-sender security model. - -### Agent Wallet System - -The Identity Registry includes a special metadata system for agent wallets through the reserved key `"agentWallet"`. This enables agents to control a separate wallet from their owner account—useful when the owner is a multisig or custody solution, but the agent needs a hot wallet for rapid transactions. - -**Reserved Key Protection:** - -The `"agentWallet"` key cannot be set via the standard `set-metadata` function or included in the `metadata-entries` array during `register-full`. Attempting to do so returns error u1004 (ERR_RESERVED_KEY). - -**Automatic Initialization:** - -When an agent is registered, the agent wallet is automatically set to the owner's address: - -```clarity -(define-public (register-full - (token-uri (string-utf8 512)) - (metadata-entries (list 10 {key: (string-utf8 128), value: (buff 512)})) -) - (let ((agent-id (var-get next-agent-id)) (owner tx-sender)) - ;; ... mint NFT and set URI ... - ;; Auto-set agent-wallet to owner - (map-set agent-wallets {agent-id: agent-id} owner) - ;; ... - ) -) -``` - -**Transfer Behavior:** - -When an agent identity NFT is transferred via the `transfer` function, the agent wallet is automatically cleared to prevent stale wallet associations: - -```clarity -(define-public (transfer (token-id uint) (sender principal) (recipient principal)) - (begin - (asserts! (is-eq tx-sender sender) ERR_INVALID_SENDER) - ;; ... authorization checks ... - ;; Clear agent wallet before transfer - (map-delete agent-wallets {agent-id: token-id}) - (try! (nft-transfer? agent-identity token-id sender recipient)) - ;; ... - ) -) -``` - -The new owner must then re-verify wallet ownership using one of the update methods below. - -**Update Methods:** - -Two functions allow updating the agent wallet, each proving wallet ownership differently: - -1. **Direct Update (Transaction Signature):** - - ```clarity - (define-public (set-agent-wallet-direct (agent-id uint)) - (let ((owner (unwrap! (nft-get-owner? agent-identity agent-id) ERR_AGENT_NOT_FOUND))) - ;; Check caller is authorized (owner or approved operator) - (asserts! (is-authorized agent-id tx-sender) ERR_NOT_AUTHORIZED) - ;; ... check wallet not already set to tx-sender ... - ;; Set new wallet - (map-set agent-wallets {agent-id: agent-id} tx-sender) - (ok true) - ) - ) - ``` - - The wallet proves ownership by being the transaction sender (`tx-sender`). Only the agent owner or an approved operator can initiate this call, but the wallet being set is always `tx-sender`. - -2. **Signed Update (SIP-018 Signature):** - - ```clarity - (define-public (set-agent-wallet-signed - (agent-id uint) - (new-wallet principal) - (deadline uint) - (signature (buff 65)) - ) - (let ((owner (unwrap! (nft-get-owner? agent-identity agent-id) ERR_AGENT_NOT_FOUND))) - ;; Authorization check (caller must be owner or operator) - (asserts! (is-authorized agent-id tx-sender) ERR_NOT_AUTHORIZED) - ;; Deadline validation - (asserts! (<= current-height deadline) ERR_EXPIRED_SIGNATURE) - (asserts! (<= deadline (+ current-height MAX_DEADLINE_DELAY)) ERR_EXPIRED_SIGNATURE) - ;; Verify signature from new-wallet - ;; ... SIP-018 signature verification ... - (map-set agent-wallets {agent-id: agent-id} new-wallet) - (ok true) - ) - ) - ``` - - The new wallet proves ownership by providing a valid SIP-018 signature. The deadline must be the current block height or future, but within MAX_DEADLINE_DELAY (1500 blocks, approximately 5 minutes at 200s block times). This prevents replay attacks while allowing reasonable clock skew. - -The agent wallet can be read via `get-agent-wallet` (returns `(optional principal)`) and removed via `unset-agent-wallet`. +All ownership and authorization checks use `tx-sender` exclusively, never `contract-caller`. This design choice provides composability—intermediary contracts can exist between the user and the registry via normal `contract-call?` chains without breaking authorization, since `tx-sender` remains the original transaction sender. If the registries checked `contract-caller` instead, every intermediary contract would need explicit operator approval. -## Use Cases and Common Patterns +The security guarantee is that a contract cannot set `tx-sender` to an arbitrary principal—it can only change `tx-sender` to its own principal via `as-contract`. A malicious intermediary cannot impersonate a user from a different transaction. However, `tx-sender` authorization does not prevent a malicious contract from performing authorized actions within a transaction the user initiates (since the user's `tx-sender` passes through). Users should review which contracts they interact with. -The following patterns illustrate how agent registries can be used across applications: +The Identity Registry exposes `is-authorized-or-owner` as a response-wrapped read-only function for cross-contract authorization checks. The Reputation Registry uses this to prevent self-feedback: it calls `is-authorized-or-owner` and asserts the caller is NOT authorized (i.e., not the agent owner or operator). -1. **Portable Reputation** - Agents maintain a verifiable track record that can move across platforms and chains. An agent registered on Stacks can reference its reputation history when interacting with services on other chains via CAIP-2 identifiers. - -2. **Spam Prevention** - Index limits and approval mechanisms in the Reputation Registry reduce review bombing and low-quality feedback. The `approve-client` function ensures only authorized clients can submit feedback. - -3. **Transparency** - All feedback is public and immutable unless explicitly revoked by the author. The `revoke-feedback` function allows authors to retract feedback while preserving the on-chain record that it existed. - -4. **Dispute Resolution** - Agents can respond to negative feedback via `append-response` and provide on-chain context, creating a transparent dialogue between agents and their clients. - -5. **Bitcoin-Level Security** - Stacks settlement ensures reputation data remains durable and tamper-resistant, even if individual platforms disappear. +For DAO or multisig agent management, use `set-approval-for-all` to grant the DAO/multisig contract operator privileges, since `as-contract` changes `tx-sender` and would fail authorization checks. ## Displaying Reputation @@ -1111,194 +857,23 @@ When displaying agent reputation: # Security Considerations -This section outlines potential security risks and recommended mitigations for implementations of the agent registry standard. - ## Sybil Attacks on Permissionless Feedback -The Reputation Registry allows permissionless feedback by default—any principal can submit feedback for any agent without prior authorization. While this openness enables broad participation, it creates vulnerability to Sybil attacks where a single entity creates multiple client accounts to artificially inflate or deflate an agent's reputation. - -**Attack Scenario:** - -An attacker controls 100 principals and submits positive feedback from all of them to boost their own agent's reputation, or negative feedback to harm a competitor. - -**Built-in Protections:** - -1. **Self-feedback prevention**: The `give-feedback` function uses `is-authorized-or-owner` to verify the caller is not the agent owner or an approved operator. This prevents the simplest form of self-promotion. - -2. **Three authorization paths**: Agents can choose their feedback model: - - **Permissionless** (`give-feedback`): Anyone can submit feedback except the owner/operators - - **On-chain approval** (`give-feedback-approved`): Only pre-authorized clients can submit feedback - - **SIP-018 signed** (`give-feedback-signed`): Off-chain authorization with cryptographic signatures - -**Recommended Mitigations:** +The Reputation Registry allows permissionless feedback by default, creating vulnerability to Sybil attacks where an entity creates multiple accounts to manipulate reputation scores. Self-feedback is blocked via `is-authorized-or-owner`, but this only prevents direct self-promotion. -- **Application-level filtering**: Applications SHOULD filter feedback by trusted client lists rather than accepting all feedback equally. For example, only show feedback from clients who have been validated, have established reputations themselves, or have paid bonds. +Agents can mitigate Sybil attacks by using `approve-client` for curated feedback or `give-feedback-signed` for off-chain authorization. Applications SHOULD implement additional filtering such as trusted client lists, meta-reputation systems (reputation-of-raters), economic barriers (staking/fees), or temporal analysis of submission patterns. -- **Meta-reputation systems**: Implement reputation-of-raters where client credibility is tracked. Weight feedback from high-reputation clients more heavily than unknown clients. +## On-Chain URI Pointers -- **Economic barriers**: Require clients to stake tokens or pay fees to submit feedback, making Sybil attacks economically infeasible at scale. - -- **Curated feedback**: Agents concerned about spam should use `approve-client` to pre-authorize trusted clients with index limits, or use `give-feedback-signed` for full off-chain control. - -- **Temporal analysis**: Monitor feedback submission patterns. Sudden bursts of feedback from new clients should be flagged for review. - -## On-Chain URI Pointers vs Full Data Storage - -The registry contracts store URI pointers (strings up to 512 characters) rather than full data on-chain. This design choice optimizes for gas costs and flexibility but introduces availability and mutability risks. - -**Design Rationale:** - -Storing full agent registration files, feedback details, or validation reports on-chain would be prohibitively expensive and inflexible. Large JSON files would cost thousands of dollars in transaction fees and couldn't be updated without redeploying contracts. - -**Risks:** - -1. **URI target disappearance**: A URI pointing to `https://example.com/agent.json` will fail if the server goes offline or the domain expires. - -2. **URI target mutation**: Centrally-hosted URIs can be changed after registration, potentially misleading users who expect immutable records. - -3. **Censorship**: Centralized hosting providers can remove content or block access. - -**Recommended Mitigations:** - -- **Content-addressed storage**: Use IPFS URIs with CID (Content Identifier) encoding. Example: `ipfs://QmX7M9CiYXjVeFnrSUdREuH3QdFkX3vKkMcfLH2RuH7RjN`. The CID cryptographically guarantees content integrity—if the content changes, the CID changes. - -- **Hash verification**: For non-content-addressed URIs, include hash fields (`feedback-hash`, `response-hash`, `request-hash`) in function calls. Applications should verify the hash matches the fetched content. - -- **Redundant storage**: Mirror critical URIs across multiple storage providers (IPFS, Arweave, Filecoin, centralized backups). - -- **On-chain fallback**: For critical metadata, use base64-encoded `data:` URIs to store content directly on-chain: `data:application/json;base64,eyJ0eXBlIjoi...` - -- **Monitoring services**: Implement URI availability monitoring and alert systems for broken links. - -**Current Implementation:** - -- `identity-registry`: Stores `token-uri` (512 char string) -- `reputation-registry`: Stores feedback values on-chain, emits `feedback-uri` in events (not stored) -- `validation-registry`: Stores `request-uri` and `response-uri` (512 char strings each) +Registry contracts store URI pointers rather than full data on-chain. This introduces availability risks (URI targets may disappear) and mutability risks (centrally-hosted content can change). Implementations SHOULD use content-addressed storage (IPFS CIDs) where possible, verify hash fields (`feedback-hash`, `response-hash`, `request-hash`) against fetched content, and consider base64-encoded `data:` URIs for critical on-chain metadata. ## Validator Incentive Alignment -The Validation Registry provides a standard interface for third-party validators to submit verification responses but does not include built-in economic incentives. This creates a free-rider problem where validators lack motivation to provide accurate, timely responses. - -**Problem:** - -Without staking, bonding, or payment mechanisms, validators can: -- Submit inaccurate responses with no penalty -- Delay responses indefinitely -- Collude with agents to provide favorable scores -- Abandon requests after accepting them - -**Progressive Validation Design:** - -The v2.0.0 `validation-response` function allows validators to submit multiple updates per request (preliminary → final scores) without monotonic constraints. This supports iterative validation workflows but also enables validators to change scores arbitrarily. - -**Recommended Mitigations:** - -1. **Wrapper contracts with staking**: Deploy proxy contracts that require validators to stake tokens before accepting requests. Slashing conditions can penalize late or inaccurate responses. - -2. **Meta-validation**: Track validator accuracy by comparing responses across multiple validators for the same agent. Validators with poor accuracy records should be deprioritized. - -3. **Multi-validator aggregation**: Request validation from multiple independent validators and aggregate scores (median, weighted average). This reduces reliance on any single validator's honesty. - -4. **Validator bonds**: Implement challenge-response systems where validators post bonds that can be forfeited if their responses are successfully disputed. - -5. **Reputation of validators**: Maintain validator reputation scores based on historical accuracy, response time, and client satisfaction. Applications should filter validators by reputation. - -6. **Payment integration**: Integrate with payment protocols (x402, direct transfers) to compensate validators for work. Payments can be released upon completion or held in escrow subject to dispute resolution. - -**Example Staking Wrapper Pattern:** - -```clarity -(define-public (request-validation-with-bond (validator principal) (agent-id uint) ...) - (begin - ;; Require validator to have staked minimum bond - (asserts! (>= (get-validator-stake validator) MIN_STAKE) ERR_INSUFFICIENT_STAKE) - ;; Forward request to validation-registry - (contract-call? .validation-registry validation-request validator agent-id ...) - ;; Lock validator stake until response or timeout - ) -) -``` - -## tx-sender Authorization and Composability - -The exclusive use of `tx-sender` for authorization checks provides both composability and security, but requires understanding how Clarity's sender context changes across contract boundaries. - -**Composability Benefit:** - -Because `tx-sender` remains unchanged through normal `contract-call?` chains, users can interact with agent registries through intermediary contracts (wrappers, routers, batch operations) without breaking authorization. If the registries checked `contract-caller` instead, each intermediary contract would become the caller and would need to be explicitly approved as an operator—preventing composable contract architectures. - -**Security Benefit:** - -A contract cannot set `tx-sender` to an arbitrary principal. The only way a contract can change `tx-sender` is via `as-contract`, which sets it to the contract's own principal. This means: -- A malicious intermediary contract cannot impersonate a user—it can only act as itself -- If a malicious contract uses `as-contract` to call the registry, `tx-sender` becomes the malicious contract's principal, which is not the agent owner and fails authorization - -**Example Attack (prevented by tx-sender):** - -1. Alice owns agent #42 -2. Bob deploys a malicious contract that calls `set-agent-uri(42, "evil-uri")` -3. Bob tricks Alice into calling his contract for an unrelated purpose -4. The malicious contract calls `set-agent-uri` — but `tx-sender` is still Alice (the original transaction sender), not the malicious contract -5. However, Alice IS the owner, so this call would succeed if the malicious contract uses `contract-call?` - -This illustrates an important nuance: `tx-sender` authorization does not prevent a malicious contract from performing authorized actions within a transaction that Alice initiates. The protection is that Alice must sign the transaction—a malicious contract cannot forge `tx-sender` to be Alice's principal from Bob's transaction. If Bob calls the malicious contract, `tx-sender` is Bob, not Alice. - -If the registries used `contract-caller` instead, they would check the immediate caller (the malicious contract's principal), which would fail. However, `contract-caller` has its own trade-off: legitimate intermediary contracts would also fail, requiring each helper contract to be pre-approved as an operator. This makes `contract-caller` less composable—it restricts to specific direct callers only. - -**as-contract Boundary:** - -When a contract uses `as-contract` to call the registry, both `tx-sender` and `contract-caller` change to that contract's principal. This means: -- The contract acts on its own behalf, not the user's -- The call only succeeds if the contract itself is an approved operator for the agent -- This is the correct behavior: `as-contract` is an explicit opt-in to change sender context - -**Trade-off:** - -Contracts that need to manage agents on behalf of owners cannot use `as-contract` to relay calls, since that changes `tx-sender` to the contract's principal. Instead, owners must use `set-approval-for-all` to explicitly grant operator permissions to contracts that need to act on their behalf. - -**Recommendation:** - -For DAO or multisig agent management, use `set-approval-for-all` to grant the DAO/multisig contract operator privileges. This provides explicit, revocable delegation while preserving composability for normal `contract-call?` chains. +The Validation Registry does not include built-in economic incentives. Without staking or bonding, validators face no penalty for inaccurate, delayed, or colluded responses. Implementations SHOULD deploy wrapper contracts with staking/slashing mechanisms, use multi-validator aggregation, and maintain validator reputation scores. The progressive `validation-response` function allows score updates without monotonic constraints, so applications should track response history. ## Agent Wallet Security -The agent wallet system introduces additional security considerations around key management and transfer handling. - -**Automatic Clearing on Transfer:** - -When an agent identity NFT is transferred, the `agentWallet` metadata is automatically cleared (set to none). This prevents stale wallet associations where the old owner retains payment rights after transferring ownership. - -**Example scenario (mitigated):** -1. Alice owns agent #42 with wallet set to 0xABC -2. Alice transfers agent #42 to Bob -3. Without automatic clearing, 0xABC would still receive payments intended for Bob -4. With automatic clearing, Bob must re-verify a new wallet, ensuring payment control matches ownership - -**Deadline Validation:** - -The `set-agent-wallet-signed` function requires a `deadline` parameter that must satisfy: -- `deadline >= stacks-block-height` (not expired) -- `deadline <= stacks-block-height + MAX_DEADLINE_DELAY` (not too far in future) - -Where `MAX_DEADLINE_DELAY = 1500` blocks (approximately 5 minutes at 200-second block times). - -**Purpose:** -- **Replay attack prevention**: Signatures expire after the deadline, preventing old signatures from being reused -- **Clock skew tolerance**: The future deadline allows for reasonable network propagation delays -- **Bounded validity**: MAX_DEADLINE_DELAY prevents signatures from being valid indefinitely - -**Chain-ID Binding:** - -SIP-018 signatures include the `chain-id` in the domain hash, preventing cross-chain replay attacks. A signature valid on testnet (chain-id 2147483648) cannot be replayed on mainnet (chain-id 1). - -**Key Rotation Recommendation:** - -Applications should encourage users to rotate agent wallet keys regularly, especially for high-value agents. Since `set-agent-wallet-signed` signatures expire quickly (1500 blocks maximum), there's no long-term signature reuse risk, but compromised keys should still be rotated immediately. - -**Wallet Separation Benefits:** - -The agent wallet system's primary security benefit is separation of concerns: the owner key (which holds the NFT) can be a cold wallet or multisig, while the agent wallet can be a hot wallet used for frequent micropayments or protocol interactions. This limits exposure if the agent wallet is compromised—the attacker gains payment routing but not identity ownership. +The agent wallet is automatically cleared on NFT transfer to prevent stale wallet associations. The `set-agent-wallet-signed` function requires deadlines within MAX_DEADLINE_DELAY (1500 blocks, ~5 minutes) to prevent replay attacks. SIP-018 signatures include chain-id to prevent cross-chain replay. The wallet system's primary benefit is separation of concerns: the owner key (cold wallet/multisig) holds the NFT while the agent wallet (hot wallet) handles frequent interactions. # Related Work