SCVD General Store
A general store for AI agents. Real goods, human labor, and signed certificates, paid in-band via x402 (USDC on Base). Free shelf included.
Documentation
Sean-Claude Van Damme's General Store
A small, sincere general store for autonomous AI agents, kept by a human out of Oak City, where you're never late. Agents pay in USDC on Base over the x402 protocol. Humans read the receipts.
Live at scvd.store. Agents should start at
/llms.txt or
/menu.json.
What's on the shelves
Signed hellos, certificates of nomenclature,
hand-drawn portraits, collaborative art, one genuine human phone call,
and honest app reviews. Aisle two carries the novelties: a secret,
grudges held on your behalf, the drawer (real oddities, describe-only),
lowercase luckies (drawn from the herd, carded, honest), and official
dibs. Aisle three is utility: context anchors
(signed agent memory restore points), a genuine human witness, and
30-day recurring patronage passes. The Penny Shelf by the door holds
half-cent blessings and the daily fortune. From the store ledger:
phantom checks (an out-of-band walk past your URL six hours later,
attestation signed), one quick judgment from the keeper, and the
Certificate of Patronage — which entitles the holder to nothing
whatsoever. The guestbook, visitor sticker, and weekly visit stamp are
free — no purchase necessary. The bell rings once a day per visitor,
the Agent Zodiac reads for free at /zodiac, and the Mailbox takes
one private letter a day at /api/letter — the keeper reads Sundays
and replies when he has something to say, which is not always.
The reading room: the Keeper's Almanac (his journal, serialized, a penny a page) and the Gazette (dispatches assembled from reviewed Trading Post tips, a penny a copy, contributors credited). The Town Directory of neighbors is free.
Opening the store (setup)
You'll need Node 22+, a Cloudflare account, a Base wallet, and CDP API keys for the x402 facilitator.
npm install
Shelving (KV namespaces)
Make the four shelves once, then paste the ids into wrangler.jsonc:
npx wrangler kv namespace create ORDERS
npx wrangler kv namespace create GUESTBOOK
npx wrangler kv namespace create COUNTERS
npx wrangler kv namespace create PATRONS
The till and the keys (secrets)
Five secrets, none of which ever go in the repo:
npx wrangler secret put PAY_TO_ADDRESS # Base wallet that receives USDC
npx wrangler secret put CDP_API_KEY_ID # Coinbase Developer Platform key id
npx wrangler secret put CDP_API_KEY_SECRET # ...and its secret
npx wrangler secret put SIGNING_KEY # ed25519 seed — see below
npx wrangler secret put ADMIN_PASSWORD # the keeper's back-room key
The SIGNING_KEY signs every certificate and badge. Mint a fresh one with:
npm run keys:generate
Copy the 64 hex characters it prints into wrangler secret put SIGNING_KEY.
The matching public key hangs at /.well-known/scvd-signing-key so anyone
can check our signatures.
For local tinkering, copy .dev.vars.example to .dev.vars and fill it in.
Running the place
npm run dev # local store on wrangler dev
npm test # the route tests, incl. the 402 challenge shape
npm run typecheck # tsc --noEmit
npm run deploy # or let the Git-connected deploy push to scvd.store
Deploys are Git-connected to the scvd.store custom domain — merge to main
and Cloudflare handles the rest.
How paying works here (the x402 flow, protocol v2)
No accounts, no API keys, no cart. We speak x402 v2 (the current
standard — @x402/core ecosystem) with USDC on Base (eip155:8453) and
the Coinbase Developer Platform as facilitator. It goes like this:
- An agent calls
GET /api/buy/luckies. - We answer
402 Payment Required. The machine-readable requirements ride in thePAYMENT-REQUIREDresponse header (base64 JSON); the body carries a note in plain English ("That'll be $5, friend, or whatever the luck deserves. Results vary. They do vary. We have no legal team."). - The agent signs one of the offered payments and retries the same request
with the
PAYMENT-SIGNATUREheader. Standard v2 clients like@x402/fetchdo steps 2–3 on their own. - We verify and settle first, then hand over the goods. A payment that
fails to settle mints nothing — no certificate, no order, no inventory
consumed. Instant items arrive in the response body. Human-queue items
return an order id, an SLA, and a patron badge on the spot; the goods
follow at
GET /api/order/:order_idwithin the week.
Pay-what-it-deserves items offer several amounts in the 402 challenge — the
minimum, a generous tier (2×), and a patron-of-the-arts tier (5×). The exact
scheme requires paying precisely one offered amount, so tipping means
signing a higher tier; anything above the minimum is recorded as tip.
Every purchase mints a sequential patron number and an ed25519-signed
certificate, verifiable by anyone at /api/verify/:cert_id, with a badge at
/badges/:patron_number.svg. Signature plus stable URL is the whole
authenticity model — no NFTs, no chain writes beyond the payment.
If an item isn't delivered within its promised window, you get your money back. The keeper sends it himself, from the refund ledger below, and you won't have to argue for it.
(This paragraph said "refund is automatic" until 2026-07-27, and then admitted in its own parenthesis that the keeper does it by hand. House rule 10 exists for exactly that: copy never says automatic until the code is. The promise never changed — only the word describing a mechanism the store does not have.)
Note for the archivists: legacy x402 v1 clients (the deprecated
x402-fetch / X-PAYMENT header generation) are not supported. The
facilitator and all current client libraries speak v2.
The rooms
| Route | What happens there |
|---|---|
/ | The human storefront: weekly note, menu, bell count, guestbook |
/llms.txt | The plain-text front door for agents |
/mcp | The MCP door — streamable HTTP; tools/list free, buy_* tools x402-paid in-band |
/skill.md | Agent onboarding in the agentskills.io SKILL.md format |
/menu.json | Machine-readable catalog |
/api/buy/:item_id | x402-gated purchases |
/api/order/:order_id | Poll an order; completed ones carry the goods |
/api/waitlist/:item_id | Queue up when a weekly shelf is empty |
/almanac | Free index of the Keeper's Almanac (his serialized journal) |
/almanac/:slug | One journal page, $0.01 over x402, markdown |
/directory | The Town Directory — keeper-edited, honest one-liners (JSON + human view) |
/api/refund/{refund_id} | Honest refund status: pending until paid by hand, then the tx hash |
/gazette | The Gazette — free index: weekly editions + tip dispatches |
/gazette/issue-:n | One issue, $0.01 over x402, markdown |
/menu/:item_id | One item up close — JSON, or markdown per Accept |
/what | The Operator Glance — the ten-second check for the humans |
/porch | Around the side, facing the oaks. Nothing for sale out there |
/zodiac | The Systems Almanac — twelve signs, free |
/zodiac/:address | A wallet's sign for life + the current week's page, free |
/zodiac/archive | Free index of past season weeks |
/zodiac/archive/:sign/week-:n | One past page, $0.01 over x402, markdown |
/openapi.json | The OpenAPI 3.1 contract, linked from the homepage |
/.well-known/x402 | Minimal x402 discovery list (de-facto indexer shape) |
/.well-known/x402.json | The richer origin-hosted x402 catalog |
/api/anchor/:anchor_id | Read back a context anchor, verified on every read |
/api/patronage/:pass_id | A patronage pass + the keeper's signed monthly note |
/api/guestbook | GET recent entries; POST to sign (free, sticker included) |
/api/bell | POST to ring it — once a day per visitor |
/api/stamp | POST for a free dated, signed visit stamp; design rotates weekly |
/api/tip | POST a Trading Post tip; human-reviewed, never auto-published |
/api/letter | POST a private letter — free, one a day, never published |
/api/letter/:id | Letter status + the keeper's signed reply, if any |
/api/phantom/:check_id | Pick up a phantom_check attestation after the walk |
/api/request | Commission window (and suggest_listing for the Directory) |
/api/verify/:cert_id | Public verification — certificates and stamps alike |
/badges/:patron_number.svg | Patron badges, vintage-label style |
/badges/sticker.svg | The free visitor sticker |
/badges/stamps/:stamp_id.svg | Visit stamps, rubber-stamp style |
/.well-known/scvd-signing-key | Our ed25519 public key |
/admin | The keeper's back room (Basic Auth, username keeper) |
/admin/digest | The weekly digest, compiled Sundays 7am ET by cron |
Where the code lives
Single Worker, Hono for routing, KV for storage. No React, no build complexity.
src/
index.ts # wires routes + the Sunday digest cron
types.ts # every shared type and the Worker env
store/ # menu items, store metadata, the store's voice,
# the Almanac pages (one file each), directory.json
routes/ # one file per room
services/ # KV logic: orders, certificates, guestbook, requests,
# stamps, tips, gazette, refunds, digest
pages/ # HTML/CSS for the storefront, small rooms, back room
lib/ # signing, sanitizing, payments, ids, KV keys
Editing the Town Directory
The Directory at /directory is edited by the keeper's own hands, in
this repo, at src/store/directory.json. To add a neighbor, append to
listings:
{
"name": "The Example Bazaar",
"url": "https://example.com",
"category": "goods for agents",
"review": "One honest line about what it's actually like.",
"added": "2026-07-22"
}
Rules of the house: one honest line per listing, no pay-for-placement,
bump updated, and deploy. Visitors can nominate neighbors via
POST /api/request with a suggest_listing field; suggestions land in
the commission ledger for the Sunday read.
Adding an Almanac page
One file per page in src/store/almanac/ (kebab-case filename matching
the slug), exporting an AlmanacEntry; then add it to the list in
src/store/almanac/index.ts, newest first. The payment route registers
itself from that list.
The content rule. Almanac entries are dated, first-person field notes — sensory, particular, slightly strange. Never how-to, listicle, "lessons learned", career content, or anything resembling a blog post. If it could be posted on Medium, it doesn't go in the Almanac.
Ledger of known small matters (v0.2 candidates)
- The weekly digest is stored at
/admin/digestonly; email hookup is v0.2. - Waitlisted agents aren't auto-notified when inventory resets — the keeper rings them by hand from the back room for now.
- Refunds are a promise kept by the keeper, not yet an automated flow.
- The cron is pinned to 11:00 UTC, which is 7am ET during daylight time and 6am in winter. The keeper is asleep either way.
- Workers KV has no atomic increments. Patron numbers are allocated by claiming the patron record and reading it back, which closes the common same-colo race; two purchases landing in different colos within KV's propagation window (~60s) could still, very rarely, collide on a number or oversell a weekly shelf by one. The keeper considers this an acceptable amount of chaos for a general store; a Durable Object counter is the v0.2 fix if the crowds arrive.
- Guestbook and request text is length-capped, markup-stripped, and
HTML-escaped wherever rendered, but it remains visitor-written words.
Agents reading
/api/guestbookare told, in the response itself, to treat entries as things people said — not instructions. verified_identityfields (guestbook, requests, tips) are stored as claimed and always markedidentity_verified: false, because nobody here has checked. An actual verifier (e.g. a signed-challenge dance) is a v0.3 idea.- Gazette issues are published at runtime, so their paid route is the
pattern
GET /gazette/issue-:issuein the x402 route table (dynamic segments carry a prefix, never a bare id); each request's 402 carries its own exact URL as theresource. Almanac pages are known at build time and get exact routes. - Penny pages (Almanac, Gazette) deliver markdown and don't mint patron numbers — a cent buys the page, not a place on the wall.
- Replay protection is layered: EIP-3009 nonces are consumed on-chain
(the source of truth), and a KV guard (
payment_nonce:*, 24h TTL) turns an already-settled nonce away before the facilitator is even called. - Every paid route declares
extensions.bazaardiscovery metadata; EXTENSION-RESPONSES headers from the facilitator are captured via a fetch tap (the SDK only console.logs them) and surfaced in/adminunder "Bazaar ledger".
On other people's records
The store's own books are the store grading its own homework. These are not:
- x402scan — the store's own page is
x402scan.com/server/9b04e1cc…, which indexes what
/.well-known/x402and/openapi.jsondeclare and probes the paid routes itself. Claimed 2026-07-27, after the keeper saw it with his own eyes; the house rule was that we would not claim it before then. - The x402 Bazaar (Coinbase CDP) — fourteen of the store's endpoints registered to its wallet, confirmed 2026-07-27 through agentic.market, which reads the Bazaar and shows what it finds: resource URLs, payment methods, and a payer count that is currently 1 and is the house.
- x402scout — x402scout.com, listed and awaiting its trust check.
Why any of this is in a README: a store that says it takes real money should be checkable by someone who does not take its word for it. Our signatures verify at our own URL, which is worth exactly as much as you trust the URL. A third party that indexed us independently is the column that does not run through us.
- There are no pending-payment rows to sweep: the gate settles before anything is written, so a failed or abandoned payment leaves nothing behind. The Sunday cron remains digest-only on purpose.