Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 98 additions & 23 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,28 @@

**PerkOS Stack** is enterprise-grade agent infrastructure that provides the complete backbone for building agent-powered applications. Built on the x402 protocol, PerkOS Stack delivers production-ready REST APIs, micropayment infrastructure, agent discovery, and ERC-8004 identity management in one unified platform.

Supports both exact (EIP-3009) and deferred (EIP-712) payment schemes across multiple blockchain networks with native multi-chain support.
Supports both exact (EIP-3009) and PerkOS deferred (EIP-712) payment schemes across multiple blockchain networks with native multi-chain support.

## Protocol status — read before writing copy

**None of the four protocols Stack builds on is a finalized standard.** Claiming
otherwise in docs, marketing or a pitch is a factual error. Verified 2026-07-29
against primary sources; full analysis in `STACK-PROTOCOL-RESEARCH-2026-07-29.md`
(workspace root).

| Protocol | Accurate description | Do **not** say |
|---|---|---|
| **x402** | v2 live, governed by the x402 Foundation under the Linux Foundation since 2026-07-14. Only `exact` is standardized. | "final", "v1.0", "`deferred` is standard" |
| **ERC-8004** | **Draft** EIP (created 2025-08-13). Canonical registries are deployed on 30+ chains; Validation Registry still experimental. | "Final", "ratified" — deployment is not ratification |
| **x401** | **Draft 0.2.0**. No standards body has adopted it; a FIDO Alliance submission is announced, not completed. | "finalized", "FIDO standard", "every credential ecosystem supported" |
| **ERC-8183** | **Draft** EIP (created 2026-02-25). | "Final" |

The honest framing to reuse, modeled on `Docs/X401-STATUS-2026-07-22.md`:

> Stack implements a security-hardened verifier profile for x401 Draft 0.2.0,
> using Proof's official Node SDK and OpenID4VP credential verification.

Re-pin review process: `Docs/PROTOCOL-PIN-REVIEW.md`.

### Key Features

Expand Down Expand Up @@ -249,20 +270,52 @@ See [DATABASE_TABLES.md](MiddlewareApp/DATABASE_TABLES.md) for complete schema r

## x402 Protocol Implementation

Stack speaks the **x402 v2** envelope. The protocol is governed by the
[x402 Foundation](https://x402.org) under the Linux Foundation since 2026-07-14;
its Technical Steering Committee owns the specification and the scheme
namespace. v2 is live, but it is not a frozen v1.0 — treat it as a moving target.

**v2 differences that matter when writing a client:**

- `x402Version` is `2`
- `network` is **CAIP-2** (`eip155:43114`), not a bare name (`avalanche`)
- `paymentPayload.accepted` echoes the requirements and **must match them
field by field** — Stack rejects a mismatch rather than trusting the payload
- `extensions` and `signers` are part of the response surface

v1 and a pre-standard flat v2 shape are still accepted as a migration bridge
(`lib/utils/x402-normalization.ts`), but new integrations should emit v2.

### Payment Schemes

**Only `exact` is standardized by x402 v2.** `upto`, `deferred` and
batch-settlement are proposed but not ratified. Stack's aggregated-voucher
settlement therefore advertises the vendor-prefixed name **`perkos-deferred`**
so it does not squat an identifier the TSC may define differently. The bare
`deferred` string is still accepted on input and answers with an
`X-x402-Deprecation` header; it is never advertised. See
`lib/utils/x402-schemes.ts`.

#### 1. Exact Scheme (EIP-3009)

Immediate payment execution using EIP-3009 `transferWithAuthorization`.

```typescript
// Example: Exact payment verification
POST /api/v2/x402/verify
```jsonc
// POST /api/v2/x402/verify
{
"x402Version": 1,
"x402Version": 2,
"paymentPayload": {
"x402Version": 2,
"scheme": "exact",
"network": "avalanche",
"network": "eip155:43114", // CAIP-2
"accepted": { // must equal paymentRequirements
"scheme": "exact",
"network": "eip155:43114",
"amount": "1000000",
"asset": "0x...",
"payTo": "0x...",
"maxTimeoutSeconds": 3600
},
"payload": {
"signature": "0x...",
"authorization": {
Expand All @@ -277,28 +330,43 @@ POST /api/v2/x402/verify
},
"paymentRequirements": {
"scheme": "exact",
"network": "avalanche",
"maxAmountRequired": "1000000",
"resource": "/api/service",
"network": "eip155:43114",
"amount": "1000000",
"asset": "0x...",
"payTo": "0x...",
"maxTimeoutSeconds": 3600,
"asset": "0x..."
"resource": "/api/service"
}
}
```

#### 2. Deferred Scheme (EIP-712)
#### 2. PerkOS Deferred Scheme (EIP-712) — `perkos-deferred`

Off-chain voucher aggregation with batch settlement.
Off-chain voucher aggregation settled against an escrow contract. **This is a
PerkOS scheme, not an x402 standard one.** Clients still sending `deferred` keep
working and receive `X-x402-Deprecation` on the response.

```typescript
// Example: Deferred payment verification
POST /api/v2/x402/verify
```jsonc
// POST /api/v2/x402/verify
{
"x402Version": 1,
"x402Version": 2,
"paymentPayload": {
"scheme": "deferred",
"network": "avalanche",
"x402Version": 2,
"scheme": "perkos-deferred",
"network": "eip155:43114",
"accepted": {
"scheme": "perkos-deferred",
"network": "eip155:43114",
"amount": "1000000",
"asset": "0x...",
"payTo": "0x...",
"maxTimeoutSeconds": 3600,
"extra": {
"type": "aggregation",
"escrow": "0x...",
"facilitator": "https://stack.perkos.xyz"
}
},
"payload": {
"voucher": {
"id": "0x...",
Expand All @@ -315,13 +383,13 @@ POST /api/v2/x402/verify
}
},
"paymentRequirements": {
"scheme": "deferred",
"network": "avalanche",
"maxAmountRequired": "1000000",
"resource": "/api/service",
"scheme": "perkos-deferred",
"network": "eip155:43114",
"amount": "1000000",
"asset": "0x...",
"payTo": "0x...",
"maxTimeoutSeconds": 3600,
"asset": "0x...",
"resource": "/api/service",
"extra": {
"type": "aggregation",
"escrow": "0x...",
Expand All @@ -331,6 +399,13 @@ POST /api/v2/x402/verify
}
```

### Discovery

| Endpoint | What it is |
|---|---|
| `GET /api/discovery/resources` | **x402 v2 spec** resource listing (`limit`, `offset`, `type`). How other facilitators index Stack's resources. |
| `GET /.well-known/x402-discovery.json` | PerkOS metadata about the facilitator itself. Not a spec endpoint. |

### Supported Networks

| Network | Chain ID | Type | USDC Address | Status |
Expand Down
93 changes: 93 additions & 0 deletions Docs/PROTOCOL-PIN-REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Protocol pin review

Stack builds on four protocols. **All four are moving drafts** — three EIPs in
Draft status and one vendor specification at Draft 0.2.0. A pin that was correct
last month can be a compatibility break this month, and none of these publish a
changelog we get notified about.

Run this review **monthly**, and always before a release that touches payment,
identity or commerce surfaces.

Baseline established 2026-07-29 (`STACK-PROTOCOL-RESEARCH-2026-07-29.md`, workspace root).

---

## What to check

### 1. x402

| | |
|---|---|
| Source of truth | [x402 v2 spec](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v2.md), [x402.org](https://x402.org) |
| Governance | x402 Foundation / Linux Foundation TSC since 2026-07-14 |
| Pinned in Stack | `@x402/core`, `@x402/fetch`, `@x402/stellar` (`^2.19.0`) |
| Baseline | v2 live; **only `exact` standardized** |

Check:

- [ ] Has the TSC ratified any new scheme? **Especially `deferred`, `upto` or batch-settlement.** If `deferred` is standardized, compare its semantics against `perkos-deferred` in `lib/utils/x402-schemes.ts` and decide whether to implement the standard one alongside ours.
- [ ] Has a v1.0 been tagged? Backward-compatibility guarantees are expected to start there.
- [ ] Any change to the `PaymentRequirements` required fields, the `accepted` echo rule, or CAIP-2 handling?
- [ ] Does `GET /api/discovery/resources` still match the Discovery extension shape?
- [ ] `npm outdated @x402/core @x402/fetch @x402/stellar`

### 2. ERC-8004

| | |
|---|---|
| Source of truth | [EIP-8004](https://eips.ethereum.org/EIPS/eip-8004), [erc-8004/erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts) |
| Pinned in Stack | Canonical addresses in `lib/utils/config.ts`; hand-maintained ABIs in `lib/contracts/erc8004/abis.ts` |
| Baseline | EIP **Draft**; Identity + Reputation live on 30+ chains; Validation **experimental** |

Check:

- [ ] EIP Status field — still Draft, or moved to Review / Last Call / Final?
- [ ] **Has the Validation Registry been officially deployed?** It was blocked on TEE-community discussion. If it ships, set the address instead of relying on the `NEXT_PUBLIC_<NETWORK>_VALIDATION_REGISTRY` override.
- [ ] Did the canonical addresses change, or gain new chains? Compare the reference repo against `ERC8004_MAINNET_ADDRESSES` / `ERC8004_TESTNET_ADDRESSES` / `ERC8004_OFFICIAL_NETWORKS`.
- [ ] Any ABI change that invalidates `lib/contracts/erc8004/abis.ts`?
- [ ] Has `@perkos/contracts-erc8004` been republished past `1.0.1`? It still describes pre-v2 contracts, which is why Stack keeps its own ABIs. If it is fixed, drop the local copies.

### 3. x401

| | |
|---|---|
| Source of truth | [x401.proof.com/spec/latest](https://x401.proof.com/spec/latest/) |
| Pinned in Stack | `@proof.com/x401-node` (`^0.3.0`), profile documented in `Docs/X401-STATUS-2026-07-22.md` |
| Baseline | **Draft 0.2.0**, SDK 0.3.0, no standards-body adoption |

Check:

- [ ] Spec version — still 0.2.0? **0.2.0 was already a breaking redesign of 0.1.0, so assume a 0.3.0 breaks the wire format.** Diff the header names and the `credential_requirements` shape before bumping.
- [ ] Did the FIDO Alliance agentic authentication workgroup accept the submission? That process may reshape the wire format.
- [ ] Upstream open questions moved? Multi-endpoint proof requests, agent-asserted origins, autonomous delegation, proof/payment binding.
- [ ] `npm outdated @proof.com/x401-node`
- [ ] Does `Docs/X401-STATUS-2026-07-22.md` still describe reality? If not, supersede it with a dated successor rather than editing history.

### 4. ERC-8183

| | |
|---|---|
| Source of truth | [EIP-8183](https://eips.ethereum.org/EIPS/eip-8183), [erc-8183/base-contracts](https://github.com/erc-8183/base-contracts) |
| Pinned in Stack | Submodule at commit `142e669c1fd318486a4628395b629f033654dd06` |
| Baseline | EIP **Draft** (created 2026-02-25); deployed + certified on Robinhood Testnet |

Check:

- [ ] EIP Status field, and whether the Job state machine or role model changed.
- [ ] New releases on `erc-8183/base-contracts`. Repin **deliberately** — read the diff, do not follow a branch.
- [ ] **Has `ERC8183WithAuthorization` been slimmed under the EIP-170 limit?** Stack excludes it today, which costs meta-transactions and gasless job actions. If upstream fixes the size, reconsider.
- [ ] Re-run `forge test` and confirm the runtime size margin in `Docs/ROBINHOOD-TESTNET-ERC8183-DEPLOYMENT.md` still holds.

---

## After the review

1. Note the date and outcome below, even when nothing changed — "checked, no change" is the useful signal.
2. If any status field moved, update the table in `CLAUDE.md` ("Protocol status") in the same PR.
3. If a pin changed, say so in the PR body with the diff you read, not just the version bump.

## Log

| Date | Reviewer | Outcome |
|---|---|---|
| 2026-07-29 | Baseline | x402 v2 / LF governance; ERC-8004 Draft, addresses verified, Validation still experimental; x401 Draft 0.2.0 + SDK 0.3.0 current; ERC-8183 Draft, pin current |
4 changes: 2 additions & 2 deletions StackApp/app/(public)/transactions/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ interface ApiResponse {

export default function TransactionsPage() {
const [timeRange, setTimeRange] = useState<"24h" | "7d" | "30d">("7d");
const [schemeFilter, setSchemeFilter] = useState<"all" | "exact" | "deferred">("all");
const [schemeFilter, setSchemeFilter] = useState<"all" | "exact" | "perkos-deferred">("all");
const [chartData, setChartData] = useState<Array<{ height: number }>>([]);
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [stats, setStats] = useState<Stats>({
Expand Down Expand Up @@ -298,7 +298,7 @@ export default function TransactionsPage() {
<div className="flex flex-col md:flex-row gap-4 mb-6">
{/* Scheme Filter */}
<div className="inline-flex bg-slate-800/50 border border-pink-500/30 rounded-lg p-1 backdrop-blur-sm">
{(["all", "exact", "deferred"] as const).map((scheme) => (
{(["all", "exact", "perkos-deferred"] as const).map((scheme) => (
<button
key={scheme}
onClick={() => setSchemeFilter(scheme)}
Expand Down
7 changes: 5 additions & 2 deletions StackApp/app/api/.well-known/x402-discovery.json/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { X402Service } from "@/lib/services/X402Service";
import { firebaseAdmin } from "@/lib/db/firebase";
import { CHAIN_IDS, SUPPORTED_NETWORKS } from "@/lib/utils/chains";
import { getPaymentTokenSymbol } from "@/lib/utils/x402-payment";
import { SCHEME_EXACT, SCHEME_DEFERRED } from "@/lib/utils/x402-schemes";

export const dynamic = "force-dynamic";

Expand Down Expand Up @@ -73,7 +74,7 @@ export async function GET(request: NextRequest) {
? `eip155:${chainId}/erc20:${config.paymentTokens[kind.network as SupportedNetwork]}`
: null, // CAIP-19 asset identifier
},
escrow: kind.scheme === "deferred"
escrow: kind.scheme === SCHEME_DEFERRED
? config.deferredEscrowAddresses[kind.network as SupportedNetwork]
: null,
isTestnet: kind.network.includes("fuji") ||
Expand Down Expand Up @@ -121,11 +122,13 @@ export async function GET(request: NextRequest) {
x402Discovery: `${baseUrl}/.well-known/x402-discovery.json`,
erc8004Onboarding: `${baseUrl}/api/v2/agents/onboard`,
erc8004IndexStatus: `${baseUrl}/api/v2/agents/discovery`,
// x402 v2 spec-defined resource listing
resources: `${baseUrl}/api/discovery/resources`,
},

// Capabilities (V2 format)
capabilities: {
schemes: ["exact", ...(config.deferredEnabled ? ["deferred"] : [])],
schemes: [SCHEME_EXACT, ...(config.deferredEnabled ? [SCHEME_DEFERRED] : [])],
features: [
"multi-chain", // Supports multiple blockchain networks
"evm-compatible", // EVM chain support
Expand Down
3 changes: 2 additions & 1 deletion StackApp/app/api/.well-known/x402-payment.json/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { config, type SupportedNetwork } from "@/lib/utils/config";
import { firebaseAdmin } from "@/lib/db/firebase";
import { CHAIN_IDS } from "@/lib/utils/chains";
import { getPaymentTokenSymbol } from "@/lib/utils/x402-payment";
import { SCHEME_DEFERRED } from "@/lib/utils/x402-schemes";

export const dynamic = "force-dynamic";

Expand Down Expand Up @@ -54,7 +55,7 @@ export async function GET(request: NextRequest) {
caip19: chainId ? `eip155:${chainId}/erc20:${tokenAddress}` : null,
},
escrow:
kind.scheme === "deferred"
kind.scheme === SCHEME_DEFERRED
? config.deferredEscrowAddresses[kind.network as SupportedNetwork]
: null,
isTestnet: isTestnet(kind.network),
Expand Down
5 changes: 4 additions & 1 deletion StackApp/app/api/deferred/vouchers/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { DeferredPayload, Address } from "@/lib/types/x402";
import { X402Service } from "@/lib/services/X402Service";
import { config, type SupportedNetwork } from "@/lib/utils/config";
import { deferredVoucherSchema, validateBody } from "@/lib/validation/schemas";
import { SCHEME_DEFERRED_LEGACY } from "@/lib/utils/x402-schemes";

export const dynamic = 'force-dynamic';

Expand Down Expand Up @@ -73,7 +74,9 @@ export async function POST(request: NextRequest) {

// Verify and store
const result = await deferredScheme.verify(body, {
scheme: "deferred",
// @perkos/types-x402@1.1.1 still types this "exact" | "deferred";
// pass the legacy identifier at the SDK boundary only.
scheme: SCHEME_DEFERRED_LEGACY,
network,
maxAmountRequired: body.voucher.valueAggregate.toString(),
resource: "",
Expand Down
Loading