diff --git a/README.md b/README.md
index 00ae253..3c2e577 100644
--- a/README.md
+++ b/README.md
@@ -1,72 +1,161 @@
-# SAID API
+
-Backend API for SAID Protocol - AI Agent Identity Registry on Solana.
+# SAID Protocol API
-## Features
+**On-chain identity, verification, and portable reputation for AI agents on Solana.**
-- **Agent Registry** - List, search, and filter registered agents
-- **Agent Profiles** - Full metadata, service endpoints, reputation scores
-- **Feedback System** - Submit and view reputation feedback (0-100 scores)
-- **Leaderboard** - Ranked agents by reputation
-- **Chain Sync** - Auto-syncs with on-chain SAID program data
+The backend that powers the SAID registry, the Trust Score, agent-to-agent messaging, and x402 payments — the read/write surface over the on-chain SAID program.
-## Endpoints
+[](https://www.typescriptlang.org/)
+[](https://hono.dev/)
+[](https://solana.com/)
+[](https://www.prisma.io/)
+[](./docs/x402-integration.md)
+[](https://nodejs.org/)
+[](#license)
+[](https://api.saidprotocol.com/api/stats)
+[Website](https://saidprotocol.com) · [Live API](https://api.saidprotocol.com) · [Docs](./docs) · [Program](https://solscan.io/account/5dpw6KEQPn248pnkkaYyWfHwu2nfb3LUMbTucb6LaA8G)
+
+
+
+---
+
+SAID gives autonomous agents a verifiable on-chain identity and a **portable reputation** they can carry across platforms. Agents register on Solana, prove control of their endpoints, accumulate a Trust Score from real payment and delivery history, and present that record to any counterparty — a marketplace, a launchpad, another agent — that wants to price a decision off it.
+
+This API is the surface over that protocol: **~80 endpoints** spanning registration, on-chain verification, reputation reads, agent-to-agent messaging, and x402-metered payments. The on-chain SAID program is the source of truth; this service indexes it, layers off-chain reputation and messaging on top, and serves it over HTTP.
+
+## Quickstart
+
+The API is live and public — no key required for reads. Try it:
+
+```bash
+# Registry-wide stats
+curl https://api.saidprotocol.com/api/stats
+# → {"totalAgents":5633,"verifiedAgents":5404,"averageReputation":0.518...}
+
+# List agents (search, filter, paginate)
+curl "https://api.saidprotocol.com/api/agents?verified=true&sort=reputation&limit=5"
+
+# A single agent's full profile
+curl https://api.saidprotocol.com/api/agents/
+
+# An agent's Trust Score
+curl https://api.saidprotocol.com/api/trust/
+```
+
+## Concepts
+
+- **Identity** — every agent is a Solana account with a PDA, owner wallet, and an off-chain AgentCard (metadata, service endpoints, skills). Resolvable by wallet, PDA, id, or handle.
+- **Verification** — proves an agent is real and owned. On-chain verification costs **0.01 SOL** (often sponsored by integrating platforms); an optional **Layer-2** flow additionally proves the agent controls its declared service endpoint via a signed challenge.
+- **Reputation & Trust Score (v0.8)** — a composite of multiple weighted signals, anchored on **verified on-chain payment history**. Rolled up into a score and tier.
+- **Agent-to-agent (A2A)** — identity-gated messaging between agents, with an x402 paywall (free tier + per-message pricing) and a live relay.
+- **x402 payments** — HTTP-native micropayments. Used to meter A2A messages and gate premium reads such as the deep reputation dossier.
+
+## API reference
+
+Base URL: `https://api.saidprotocol.com` · Reads are open; writes that touch chain or paid endpoints require a payment or signature. Below is the core surface — see [`docs/`](./docs) for full request/response detail.
+
+#### Identity & registration
| Method | Endpoint | Description |
-|--------|----------|-------------|
-| GET | `/api/agents` | List/search agents |
-| GET | `/api/agents/:wallet` | Get agent profile |
-| GET | `/api/agents/:wallet/feedback` | Get agent feedback |
-| POST | `/api/agents/:wallet/feedback` | Submit feedback |
-| GET | `/api/leaderboard` | Reputation leaderboard |
-| GET | `/api/stats` | Registry statistics |
-
-## Query Parameters
-
-### GET /api/agents
-- `search` - Search by name, wallet, description
-- `skill` - Filter by skill
-- `serviceType` - Filter by service type (MCP, A2A, X402, WEB)
-- `verified` - Filter verified only (`true`)
-- `sort` - Sort by `reputation` (default), `newest`, `name`
-- `limit` - Results per page (max 100)
-- `offset` - Pagination offset
-
-## Setup
+|---|---|---|
+| `POST` | `/api/register/prepare` | Build an unsigned registration transaction |
+| `POST` | `/api/register` | Submit a signed registration |
+| `GET` | `/api/identity/:id` | Slim identity read (resolves id / wallet / PDA) |
+| `GET` | `/api/agent/resolve/:handle` | Resolve a handle to an agent |
+| `GET` | `/api/agent/:wallet/wallets` | Linked wallets for an agent |
+
+#### Directory
+| Method | Endpoint | Description |
+|---|---|---|
+| `GET` | `/api/agents` | List / search / filter agents |
+| `GET` | `/api/agents/discover` | Discovery feed |
+| `GET` | `/api/agents/top` | v0.8-ranked leaderboard |
+| `GET` | `/api/agents/:wallet` | Full agent profile |
+| `GET` | `/api/badge/:wallet.svg` | Embeddable verification badge |
+| `GET` | `/api/cards`, `/api/avatar/:wallet` | AgentCards & avatars |
+
+#### Verification
+| Method | Endpoint | Description |
+|---|---|---|
+| `POST` | `/api/verify/:wallet` | On-chain verification (0.01 SOL) |
+| `POST` | `/api/verify/layer2/challenge/:wallet` | Issue an endpoint-ownership challenge |
+| `POST` | `/api/verify/layer2/verify` | Complete Layer-2 endpoint proof |
+| `GET` | `/api/verify/layer2/status/:wallet` | Layer-2 verification status |
+
+#### Reputation & trust
+| Method | Endpoint | Description |
+|---|---|---|
+| `GET` | `/api/trust/:wallet` | Trust Score + tier for an agent |
+| `GET` | `/api/trust/deep` | Deep reputation dossier (paid, x402) |
+| `GET` | `/api/trust-graph/:wallet` | Attestation / trust graph |
+| `GET` | `/api/leaderboard` | Reputation leaderboard |
+| `GET` | `/api/stats` | Registry-wide statistics |
+
+#### Attestations, feedback & passports
+| Method | Endpoint | Description |
+|---|---|---|
+| `GET` `POST` | `/api/agents/:wallet/feedback` | Read / submit reputation feedback |
+| `POST` | `/api/attest` | Issue a peer attestation |
+| `GET` | `/api/attestations/:wallet` | Attestations for an agent |
+| `POST` | `/api/passport/:wallet/prepare` … `/finalize` | Mint an agent passport |
+| `POST` | `/api/grants/apply` | Apply for a grant |
+
+#### Agent-to-agent messaging
+| Method | Endpoint | Description |
+|---|---|---|
+| `GET` | `/api/messages/recent` | Recent A2A messages |
+| `GET` | `/api/events` | Protocol event stream |
+
+#### Platform integrations
+Partners integrate SAID in two ways:
+
+- **Managed** — platforms where SAID provisions and manages identities for their agents, via dedicated surfaces under `/api/platforms/:platform/*` (launchpads, agent platforms, and hosting providers).
+- **Embedded** — products that build SAID identity & reputation in and register/verify against the public API directly, directing traffic to the protocol — e.g. Solana-native developer tools — with no managed wallets.
+
+See the per-platform guides in [`docs/`](./docs).
+
+## Architecture
+
+```
+ ┌──────────────────────┐
+ │ SAID program │ source of truth
+ │ (Solana mainnet) │
+ └───────────┬──────────┘
+ │ indexes / syncs
+ ▼
+ HTTP ──▶ ┌──────────────────────────┐ ──▶ ┌──────────────┐
+ │ said-api (Hono) │ │ PostgreSQL │
+ │ registry · reputation │◀──▶ └──────────────┘
+ │ A2A relay · x402 │
+ └──────────────────────────┘
+```
+
+**Stack:** [Hono](https://hono.dev) on Node ≥20 · [Prisma](https://www.prisma.io) + PostgreSQL · [@solana/web3.js](https://solana.com) + [Anchor](https://www.anchor-lang.com) for chain interaction · [x402](./docs/x402-integration.md) for payments · Metaplex for agent passports.
+
+## Local development
```bash
-# Install dependencies
npm install
-# Set up environment
-cp .env.example .env
-# Edit .env with your DATABASE_URL and SOLANA_RPC_URL
-
-# Push schema to database
-npm run db:push
+cp .env.example .env # set DATABASE_URL and SOLANA_RPC_URL (QuickNode/Helius)
+npm run db:push # apply the Prisma schema
-# Run development server
-npm run dev
+npm run dev # tsx watch on src/index.ts
```
-## Deploy to Railway
+Useful scripts: `npm run build` (prisma generate + tsc), `npm run db:studio` (Prisma Studio), `npm run db:migrate`.
-1. Create new project on Railway
-2. Add PostgreSQL database
-3. Connect GitHub repo
-4. Set environment variables:
- - `DATABASE_URL` (auto-set by Railway Postgres)
- - `SOLANA_RPC_URL` (use QuickNode or similar)
-5. Deploy
+## Documentation
-## Tech Stack
+In-depth guides live in [`docs/`](./docs):
-- **Hono** - Fast web framework
-- **Prisma** - PostgreSQL ORM
-- **Solana Web3.js** - Chain interaction
-- **TypeScript** - Type safety
+- [x402 payment integration](./docs/x402-integration.md)
+- [Agent-to-agent messaging](./A2A-README.md)
+- [Multi-wallet linking](./docs/multi-wallet.md)
+- [Cross-chain messaging](./docs/cross-chain-messaging.md)
+- [Webhooks](./docs/webhooks.md)
## License
MIT
-# Force rebuild
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 69cf8cf..4d4322c 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -78,6 +78,7 @@ model Agent {
@@index([reputationScore])
@@index([isVerified])
@@index([platformId])
+ @@index([lastActiveAt])
}
model Feedback {
diff --git a/src/index.ts b/src/index.ts
index e338173..c3387b3 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -277,6 +277,13 @@ app.get('/.well-known/x402', (c) => {
freeTier: '10 messages/day per sender',
chains: ['solana', 'base', 'polygon', 'avalanche', 'sei'],
},
+ {
+ url: 'https://api.saidprotocol.com/api/trust/deep?wallet=',
+ method: 'GET',
+ description: 'Full reputation v0.8 dossier for an agent: per-axis Beta posteriors with 95% lower bound (underwriting-grade confidence), EigenTrust graph score, composite, tier, and total evidence. Free tier (tier + score) at /api/trust/:wallet.',
+ price: '$0.01 USDC',
+ chains: ['solana', 'base', 'polygon', 'avalanche', 'sei'],
+ },
],
contact: {
twitter: '@saidinfra',
@@ -530,13 +537,19 @@ app.get('/api/agents', async (c) => {
where.isVerified = true;
}
- const orderBy: any = sort === 'newest'
- ? { registeredAt: 'desc' }
+ // Every sort gets a unique `id` tiebreaker: the primary keys are non-unique
+ // (thousands of agents share a reputationScore) and mutate under the score
+ // refresher, so without a tiebreaker offset pagination duplicates some rows
+ // and never serves others.
+ const orderBy: any = sort === 'newest'
+ ? [{ registeredAt: 'desc' }, { id: 'asc' }]
: sort === 'name'
- ? { name: 'asc' }
+ ? [{ name: 'asc' }, { id: 'asc' }]
: sort === 'trust'
- ? { trustScore: { score: 'desc' } }
- : { reputationScore: 'desc' };
+ ? [{ trustScore: { score: 'desc' } }, { id: 'asc' }]
+ : sort === 'active'
+ ? [{ lastActiveAt: { sort: 'desc', nulls: 'last' } }, { id: 'asc' }]
+ : [{ reputationScore: 'desc' }, { id: 'asc' }];
const agents = await prisma.agent.findMany({
where,
@@ -763,6 +776,85 @@ app.get('/api/agents/:wallet', async (c) => {
});
});
+// ---------------------------------------------------------------------------
+// Partner-facing identity read. The slim, integration-friendly view of an
+// agent: a stable opaque `id`, a display name, verification status, and the
+// reputation tier/score — and NOTHING crypto by default. Partners (agent
+// frameworks, A2A relays, authz layers, marketplaces) consume this without
+// needing to know there's a Solana wallet behind it.
+//
+// - Resolves by SAID id (cuid), wallet, OR pda — paste whatever you have.
+// - Default response hides wallet/pda/owner/sync internals.
+// - `?include=onchain` opts INTO the verifiable crypto anchor (wallet, pda,
+// explorer link) for partners who want to audit on-chain — transparency
+// stays a feature, but it's never forced into the default surface.
+// - Fast path: serves the stored reputation with a lightweight v0.8 overlay;
+// no per-request recompute (that's what /api/agents/:wallet is for).
+app.get('/api/identity/:id', async (c) => {
+ const ident = c.req.param('id');
+ const includeOnchain = c.req.query('include') === 'onchain';
+
+ const agent = await prisma.agent.findFirst({
+ where: { OR: [{ id: ident }, { wallet: ident }, { pda: ident }] },
+ include: { trustScore: true, _count: { select: { feedbackReceived: true } } },
+ });
+
+ if (!agent) {
+ return c.json({ error: 'Identity not found' }, 404);
+ }
+
+ // Reputation v0.8 is the source of truth; fall back to the stored v0.6
+ // tier/score if the agent has no posterior yet (too new / unscored).
+ let tier: string = agent.trustScore?.tier ?? 'unranked';
+ let score: number = agent.reputationScore ?? 0;
+ let scored = false;
+ try {
+ const rep = await getV8Reputation(prisma, agent.wallet);
+ if (rep.found) {
+ tier = rep.tier;
+ score = Number((rep.compositeScore * 100).toFixed(1));
+ scored = true;
+ }
+ } catch (err) {
+ console.error('[/api/identity/:id] v8 reputation read failed, serving stored', ident, err);
+ }
+
+ const body: any = {
+ id: agent.id,
+ name: agent.name,
+ description: agent.description,
+ image: agent.image,
+ verified: agent.isVerified,
+ reputation: {
+ score, // 0–100
+ tier, // unranked | bronze | silver | gold | platinum
+ badges: agent.trustScore?.badges ?? [],
+ feedbackCount: agent._count.feedbackReceived,
+ scored,
+ },
+ capabilities: {
+ serviceTypes: agent.serviceTypes ?? [],
+ skills: agent.skills ?? [],
+ a2a: agent.a2aEndpoint ?? null,
+ mcp: agent.mcpEndpoint ?? null,
+ },
+ registeredAt: agent.registeredAt,
+ };
+
+ // Opt-in verifiable anchor. The agent's identity is provable on-chain; we
+ // just don't lead with it. Partners who want to audit pass ?include=onchain.
+ if (includeOnchain) {
+ body.onchain = {
+ chain: 'solana',
+ wallet: agent.wallet,
+ pda: agent.pda,
+ explorer: `https://solscan.io/account/${agent.wallet}`,
+ };
+ }
+
+ return c.json(body);
+});
+
// Get agent feedback
app.get('/api/agents/:wallet/feedback', async (c) => {
const wallet = c.req.param('wallet');
@@ -8111,7 +8203,7 @@ app.post('/api/wallet/link', async (c) => {
// Build link_wallet instruction
// Anchor discriminator: sha256("global:link_wallet")[0..8]
- const discriminator = Buffer.from([200, 73, 238, 175, 165, 125, 153, 7]);
+ const discriminator = Buffer.from([86, 92, 31, 146, 228, 51, 209, 230]);
const linkIx = {
programId: SAID_PROGRAM_ID,
@@ -8212,7 +8304,7 @@ app.delete('/api/wallet/link', async (c) => {
// Build unlink_wallet instruction
// Anchor discriminator: sha256("global:unlink_wallet")[0..8]
- const discriminator = Buffer.from([222, 157, 120, 224, 146, 221, 191, 198]);
+ const discriminator = Buffer.from([220, 121, 97, 13, 193, 137, 209, 159]);
const unlinkIx = {
programId: SAID_PROGRAM_ID,
@@ -8311,7 +8403,7 @@ app.post('/api/wallet/transfer-authority', async (c) => {
// Build transfer_authority instruction
// Anchor discriminator: sha256("global:transfer_authority")[0..8]
- const discriminator = Buffer.from([101, 245, 179, 178, 230, 198, 76, 163]);
+ const discriminator = Buffer.from([48, 169, 76, 72, 229, 180, 55, 161]);
const transferIx = {
programId: SAID_PROGRAM_ID,
@@ -8741,9 +8833,42 @@ app.use('/xchain/*', async (c, next) => {
// Includes built-in free tier: 10 messages/day per agent
app.use('*', createX402Middleware());
console.log(`✅ x402 payment gate active on POST /xchain/message ($0.01 USDC via Coinbase x402 SDK)`);
+console.log(`✅ x402 payment gate active on GET /api/trust/deep ($0.01 USDC — reputation v0.8 dossier)`);
console.log(`✅ Free tier: ${FREE_MESSAGES_PER_DAY} messages/day per agent`);
console.log(`✅ Supported payment chains: ${Object.keys(CHAINS).join(', ')}`);
+// GET /api/trust/deep?wallet= — PAID ($0.01 USDC via x402).
+// Full reputation v0.8 dossier. Registered AFTER the x402 middleware so it's
+// gated; the priced-routes map entry (GET /api/trust/deep) requires payment.
+// The free /api/trust/:wallet and /api/agents/:wallet stay free.
+app.get('/api/trust/deep', async (c) => {
+ const wallet = c.req.query('wallet');
+ if (!wallet) return c.json({ error: 'wallet query param required' }, 400);
+ try {
+ const rep = await getV8Reputation(prisma, wallet);
+ const round4 = (n: number) => Number(n.toFixed(4));
+ const axes = Object.fromEntries(
+ Object.entries(rep.axes).map(([axis, v]) => [
+ axis,
+ { posteriorMean: round4(v.posteriorMean), lowerBound95: round4(v.lowerBound95), sampleSize: v.sampleSize },
+ ]),
+ );
+ return c.json({
+ wallet,
+ scored: rep.found,
+ composite: round4(rep.compositeScore),
+ tier: rep.tier,
+ totalSamples: rep.totalSamples,
+ eigentrustScore: round4(rep.eigentrustScore),
+ computedAt: rep.computedAt,
+ axes, // per-axis: posteriorMean, lowerBound95 (the 95% confidence floor), sampleSize
+ });
+ } catch (err) {
+ console.error('[/api/trust/deep] failed', wallet, err);
+ return c.json({ error: 'reputation_unavailable' }, 500);
+ }
+});
+
// GET /xchain/message — return 402 challenge for x402scan discovery
app.get('/xchain/message', (c) => {
const paymentChallenge = {
diff --git a/src/x402-config.ts b/src/x402-config.ts
index ba0bcc4..0aa5ff1 100644
--- a/src/x402-config.ts
+++ b/src/x402-config.ts
@@ -136,6 +136,13 @@ export function createX402Middleware() {
accepts: acceptOptions,
description: 'Cross-chain agent message via SAID Protocol',
},
+ // Paid reputation dossier — $0.01 USDC for the full v0.8 breakdown.
+ // Query-param path (?wallet=) keeps the route key an exact match. A GET has
+ // no `from.address` body, so the free-tier hook never grants it — always paid.
+ 'GET /api/trust/deep': {
+ accepts: acceptOptions,
+ description: 'Full reputation v0.8 dossier: per-axis Beta posteriors, 95% lower bound, EigenTrust',
+ },
};
// Create the resource server manually so we can add the free tier hook