Agentic spending cards: scoped, revocable payment delegations that any AI agent can plug in and pay with, fully instrumented with OpenTelemetry and SigNoz.
Issue scoped, revocable spending cards from your wallet. Any agent plugs one in and pays within your limits: no keys, no gas, dead the moment you revoke. Built on Smart Accounts (ERC-7710), settled gaslessly by 1Shot, pays the open web with x402, and plugs into any agent over MCP.
- The Idea
- How a Payment Works
- Agent Tools
- Connecting a Card to an Agent
- Architecture
- Contracts
- Observability (SigNoz)
- Getting Started
- Tests
- Environment Variables
- Security Model
- Demo Merchant
- Documentation
Agents need to spend money. Handing an agent your private key is unsafe; funding a standalone agent wallet loses custody and limits. GlassPay applies the model the card industry settled on decades ago to agents:
- Your wallet is the account. Funds never leave it until the moment of payment.
- The card is a delegation. A scoped ERC-7710 delegation, signed by your wallet, wrapped in caveats: budget per period, per-transaction max, merchant allowlist, expiry, usage count.
- The agent holds the card, not the money. The agent gets an MCP endpoint URL. Behind it, the card can spend only what its terms allow.
- Revoke kills it instantly. Freeze or revoke a card (or its whole sub-card tree) and every payment from it stops, server-side immediately and on-chain underneath.
your wallet (EIP-7702 smart account)
+-- card ($25/week, expires Jul 6) <- root delegation, signed by you
+-- agent A plugs it in over MCP
+-- sub-card ($1/week, one merchant) <- redelegation, narrower terms
+-- sub-agent B plugs it in
Live:
| Surface | URL |
|---|---|
| Dashboard (issue + manage cards) | deploy your own |
| Docs (the full reference, in-app) | deploy your own |
| Demo merchant (accepts the cards' Visas) | deploy your own |
| API + MCP endpoint | deploy your own |
| Source (this repo) | https://github.com/LSUDOKO/GlassPay |
| Demo video | YouTube |
| Docs | https://glass-pay.vercel.app/docs |
| Demo Link | https://glass-pay.vercel.app |
| Medium Blog | https://medium.com/@adoranto737/i-gave-an-ai-agent-a-credit-card-then-watched-every-move-it-made-with-signoz-d6b6cdd9d5b8?sharedUserId=adoranto737 |
Everything runs on Base mainnet with real USDC; the only simulated leg is the Visa rail (Stripe test-mode Issuing), labeled honestly wherever it appears.
- You sign in to the dashboard (Privy embedded wallet, Google login) and issue a card with terms, set by hand in the composer or drafted from a plain-language request by the Venice-powered NL compiler (the model only names tokens, protocols, and merchants; the server resolves every address from its own verified registry, and you still review and sign the draft).
- The dashboard compiles the terms into on-chain caveats (delegation-framework enforcers). Your wallet signs the delegation in the browser; the server stores it alongside a fresh agent key that holds nothing.
- You hand the card URL to any agent (one
claude mcp add, a Cursor deeplink, a pasted connector URL). - When the agent calls
pay, the server validates the terms, then redeems the delegation through the 1Shot relayer: gasless, on Base mainnet, settled in USDC from your wallet. - Every charge lands in the card's ledger with memo, fee, and tx hash.
The agent never sees a private key, never holds a balance, and never needs ETH. The first spend even deploys your wallet's 7702 smart-account code automatically in the same transaction.
MCP tools served over Streamable HTTP. The exact set a card exposes matches its capabilities, so the tool list itself is the permission surface (a pay-only card never sees execute; a contract-only card never sees pay):
| Tool | Purpose |
|---|---|
card |
Live state: remaining budget, terms, expiry, recent charges, sub-cards |
pay |
Send USDC on Base within the card's limits; blocks until confirmed on-chain |
paid_fetch |
Fetch a URL; on HTTP 402 (x402), pay automatically and return the content |
fiat_pay |
Buy over Visa rails (simulated: Stripe test-mode Issuing) against the same budget; with settlement on, the receipt carries the on-chain tx |
card_credentials |
Reveal the card's test-mode virtual Visa (number/expiry/cvc) so the agent can check out at a merchant; every card auto-links one on first need |
execute |
Run scoped contract calls (e.g. approve + swap, stake, mint) atomically in one redemption; only on cards with contract scope |
issue_subcard |
Mint a tighter child card for a sub-agent; pay caps and contract scope must both nest inside the parent's |
revoke_subcard |
Instantly kill a sub-card (and its descendants), server-side; for on-chain permanence, revoke the root card or nuke |
Refusals are typed (over_period_limit, merchant_not_allowed, price_exceeds_max, per_trade_exceeded, exceeds_parent_terms, target_not_allowed, method_not_allowed, ...) so agents can relay them honestly instead of guessing.
Contract cards. A card can be scoped to specific contract targets + method selectors instead of (or alongside) a USDC budget. The agent calls execute with either {target, method, args} (the server ABI-encodes) or {target, data} raw calldata for tuple/array/multicall methods like Uniswap exactInputSingle. For a call that needs a recipient (e.g. exactInputSingle's recipient), the card tool surfaces the card's on-chain account (the root delegator that holds the USDC and receives any output tokens), so the agent routes a swap's output there itself. Targets and selectors outside the card's declared scope are refused before anything reaches the chain, and the on-chain allowedTargets/allowedMethods enforcers check the same scope again. Method signatures are normalized to their canonical form (uint -> uint256) so the encoder, the raw-data selector check, and the on-chain enforcer all agree. Safety on contract cards is the target/method allowlist plus maxUses and expiry (contract calls are not USDC-metered); pair contract scope with a pay cap in one composite card when you want both. A contract card can also carry an allowance token list (contract.tokens: the only tokens it may approve, every approval exact-amount pinned on-chain) and a per-trade ceiling (contract.perTradeMax, capping each USDC approval; v1 enforces the ceiling on USDC legs only). Both narrow subset-only on sub-cards. Calls carry no native ETH value in v1 (the carved leaf caps value at 0 on-chain); payable-with-value is a planned extension.
Three lanes. The first two carry a per-card credential directly; the third is OAuth, where the agent never holds the card secret.
# Lane A: secret in the URL path (works everywhere, treat the URL as a password)
claude mcp add --transport http remit https://<host>/c/<card-secret>/mcp
# Lane B: bearer header
claude mcp add --transport http remit https://<host>/mcp \
--header "Authorization: Bearer <card-secret>"Lanes A and B work in Cursor, VS Code, Gemini CLI, Windsurf, claude.ai custom connectors, or any MCP client that speaks Streamable HTTP. Rotate the secret any time from the dashboard; the old URL dies instantly.
Per-harness one-liners for Lane A:
codex mcp add remit --url https://<host>/c/<card-secret>/mcp
openclaw mcp add remit --url https://<host>/c/<card-secret>/mcp --transport streamable-http # flag required: omitting it defaults to SSE
hermes mcp add remit --url "https://<host>/c/<card-secret>/mcp"
gemini mcp add -t http remit https://<host>/c/<card-secret>/mcp
goose session --with-streamable-http-extension "https://<host>/c/<card-secret>/mcp"
amp mcp add remit https://<host>/c/<card-secret>/mcp
droid mcp add remit https://<host>/c/<card-secret>/mcp --type httpclaude.ai web: Customize -> Connectors -> Add custom connector -> paste the card URL. ChatGPT Developer Mode: create a connector with the card URL as No Authentication, or use Lane C for a real auth story.
Lane C: OAuth 2.1 (card-picker consent). Add the bare endpoint with no credential:
claude mcp add --transport http remit https://<host>/mcpThe client discovers the OAuth lane (RFC 9728 protected-resource metadata on the 401), registers itself (Dynamic Client Registration), and opens a browser. You sign in with your existing dashboard login and pick which card to grant. The agent receives a short-lived, card-scoped, independently revocable access token, never the raw card secret. This is the lane OAuth-only clients such as ChatGPT require; it also works in Claude Code, claude.ai, Cursor, VS Code, Codex, Gemini CLI, Goose, opencode, Amp, and Factory Droid. Clients that complete OAuth out-of-band read the authorization code straight off the consent success screen: OpenClaw finishes with openclaw mcp login remit --code <code> (it runs no callback listener), and headless Hermes uses its paste-back flow the same way. The server is a self-hosted OAuth authorization server (public clients, PKCE S256, rotating refresh tokens); revoking the card kills every token issued for it.
Bun monorepo, three packages:
packages/
engine/ pure core: caveat compiler, issuance, spend, redelegation, revocation
server/ Hono: REST API + MCP endpoint + x402 facilitator + demo seller + Stripe webhook
dashboard/ Next.js: Privy login, one-screen cockpit (card deck + dossier, light/dark), NL issue modal (client-signed), demo shop
Key pieces:
- Caveat compiler (
engine/src/compiler.ts): turns human terms ({"pay": {"period": {"amount": "25", "seconds": 604800}}}) into delegation-framework enforcer caveats. - NL compiler (
server/src/venice/): Venice AI turns a plain-language request into a plan of named entities + numbers; the server resolves every name against its own verified registry (model output can never place an address in a draft) and assembles aCardTermsdraft for the user to review and sign. - Issuance: server prepares an unsigned delegation, the user's wallet signs it in the browser (prepare/finalize), so the server never holds the user's key for client-signed cards.
- Spend (
engine/src/spend.ts): validates terms server-side, then redeems the delegation chain through the 1Shot Public Relayer (which callsDelegationManager.redeemDelegationson-chain on your behalf), attaching the user's EIP-7702 authorization on first spend. - Sub-cards: ERC-7710 redelegations. Caps only narrow. Revoking a parent kills the subtree.
- Two payment rails off one delegation: x402 (real USDC, live) and Stripe Issuing real-time auth (test mode, fiat leg simulated honestly).
- MCP server: stateless Streamable HTTP, identity = the card credential on every request, no sessions.
- OAuth lane (
server/src/oauth/): a self-hosted OAuth 2.1 authorization server (RFC 9728 + RFC 8414 discovery, RFC 7591 dynamic client registration, PKCE S256, RFC 8707 resource binding, rotating refresh tokens, RFC 7009 revocation). Login and the card-picker consent reuse the existing Privy dashboard session; issued tokens are opaque, card-scoped, hash-stored beside the card secrets, and die when the card is revoked.
| Contract | Address |
|---|---|
| DelegationManager | 0xdb9B1e94B5b69Df7e401DDbedE43491141047dB3 |
| Stateless7702 delegator impl | 0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B |
| USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
GlassPay is fully instrumented with OpenTelemetry and sends traces, metrics, and logs to SigNoz Cloud (and can self-host locally via the included casting.yaml). The full observability architecture (16 use cases, RED metrics, SLOs, saved views, dashboards, alerts, cost control, and the service map) is documented in docs/architecture.md.
| Category | Signal | What's Tracked | How to See in SigNoz |
|---|---|---|---|
| API Requests | Trace | Every HTTP request with route pattern, method, status code, auth info | Traces -> filter service.name = glasspay-server |
| MCP Tool Calls | Trace | Every agent tool call (card, pay, shop_buy, ...) with card context and typed refusal codes |
Traces -> search name LIKE 'mcp_tool_%' |
| Stripe Webhooks | Trace | Auth decision flow (approve/decline) with decision and card context | Traces -> search stripe_webhook_auth |
| On-Chain Payments | Trace | Relayer redemption with USDC amount, gas, tx hash | Traces -> search 1shot_relayer_redeem |
| AI Compilation | Trace | Plain-language card intent -> compiled terms, token usage | Traces -> search nl_compile |
| Reconcile Sweep | Trace | Stuck-pending charge resolution (reconciled/still_pending counts) | Traces -> search reconcile_sweep |
| Fiat Settlement | Trace | Visa->on-chain settlement sweep (settled/left counts) | Traces -> search fiat_settle_sweep |
| Cards Issued | Metric | glasspay.cards_issued_total - root + sub-cards across all users |
Metrics -> counter |
| USDC Spent | Metric | glasspay.usdc_spent_total - total USDC across all rails |
Metrics -> counter |
| Active Cards | Metric | glasspay.active_cards - live gauge of issued - revoked |
Metrics -> up-down counter |
| Charges Processed | Metric | glasspay.charges_total - confirmed + pending + failed charges |
Metrics -> counter |
| API Errors | Metric | glasspay.errors_total - every 403/422/502/500 response |
Metrics -> counter |
| Refusal Logs | Log | Typed refusals with reason, card_id, attempted_amount | Logs -> filter refusal_reason |
| Card Lifecycle | Log | issued, frozen, unfrozen, revoked, nuked, url_revealed, secret_rotated, onboarded |
Logs -> filter card_event |
| Charge Confirmed | Log | Successful payments with amount, kind, card_id | Logs -> filter charge_event = confirmed |
| API Errors | Log | Every error with operation, status code, route, method | Logs -> filter operation or error_message |
glasspay-server (Node.js)
|
|- @opentelemetry/auto-instrumentations-node (automatic HTTP/fetch/DB spans)
|- Manual instrumentation via trace API (custom business spans)
|- Metrics via Meter API (counters + up-down counters)
|- Logs via Logger API (structured card lifecycle events)
|
+- OTLP HTTP exporter (port 4318)
|
v
SigNoz Cloud (ingest.us2.signoz.cloud:443)
|
|- Traces -> distributed tracing waterfall
|- Metrics -> dashboard panels + alerts
+- Logs -> structured log explorer
The OTel SDK is initialized early via Bun --preload (packages/server/src/otel.ts) so auto-instrumentation wraps every module from boot. The engine package (packages/engine/src/telemetry.ts) declares all custom metrics and structured log functions.
A casting.yaml is included for deploying SigNoz locally with Foundry:
# Deploy SigNoz stack locally
foundryctl cast -f casting.yaml --locked
# SigNoz UI: http://localhost:3301
# OTLP endpoint: http://localhost:4318
# SigNoz MCP: http://localhost:8000The casting.yaml.lock pins every Docker image to its content digest for reproducible deployments.
Create a GlassPay dashboard in SigNoz with these panels:
Panel 1: Cards Issued Over Time (Time Series)
SELECT toStartOfInterval(toDateTime(intDiv(timestamp_ms, 1000)), INTERVAL 5 MINUTE) AS ts,
sum(value) AS value
FROM signoz_metrics.distributed_samples_v2
WHERE metric_name = 'glasspay.cards_issued_total'
AND ts BETWEEN $start_datetime AND $end_datetime
GROUP BY ts
ORDER BY tsPanel 2: Active Cards (Value / Gauge)
SELECT sum(value) AS active_cards
FROM signoz_metrics.distributed_samples_v2
WHERE metric_name = 'glasspay.active_cards'
AND timestamp_ms > toUnixTimestamp(now()) * 1000 - 60000Panel 3: USDC Spent (Time Series)
SELECT toStartOfInterval(toDateTime(intDiv(timestamp_ms, 1000)), INTERVAL 5 MINUTE) AS ts,
sum(value) AS value
FROM signoz_metrics.distributed_samples_v2
WHERE metric_name = 'glasspay.usdc_spent_total'
AND ts BETWEEN $start_datetime AND $end_datetime
GROUP BY ts
ORDER BY tsPanel 4: API Errors (Time Series)
SELECT toStartOfInterval(toDateTime(intDiv(timestamp_ms, 1000)), INTERVAL 5 MINUTE) AS ts,
sum(value) AS errors
FROM signoz_metrics.distributed_samples_v2
WHERE metric_name = 'glasspay.errors_total'
AND ts BETWEEN $start_datetime AND $end_datetime
GROUP BY ts
ORDER BY tsPanel 5: API Request Duration by Route
SELECT toStartOfInterval(timestamp, INTERVAL 5 MINUTE) AS ts,
attributes_string['http.route'] AS route,
avg(durationNano) / 1000000 AS avg_ms
FROM signoz_traces.distributed_signoz_index_v2
WHERE resources_string['service.name'] = 'glasspay-server'
AND ts BETWEEN $start_datetime AND $end_datetime
GROUP BY ts, route
ORDER BY tsCreate alerts in SigNoz for these conditions:
| Alert | Condition | Severity |
|---|---|---|
| High Error Rate | glasspay.errors_total rate > 10/min for 5 min |
Critical |
| No Cards Issued | glasspay.cards_issued_total has no new value for 30 min |
Warning |
| High API Latency | P99 HTTP duration > 5000ms for 5 min | Warning |
| Spike in Refusals | Log count with refusal_reason: * > 20/min |
Warning |
GlassPay includes the SigNoz MCP server for agentic observability workflows:
# Add the SigNoz MCP server to your AI agent
claude mcp add signoz http://localhost:8000 \
--header "Authorization: Bearer $SIGNOZ_MCP_AUTH_TOKEN"Your AI agent can then use SigNoz MCP tools to query traces and logs from GlassPay, create and modify dashboards, set up and investigate alerts, and run ClickHouse queries against the observability data.
Traces
Metrics
Logs
Dashboards and Alerts
Requires bun. Real money moves on Base mainnet; use small budgets.
bun install
cp .env.example .env # then fill in the two required vars:
# GLASSPAY_MASTER_KEY=<64 hex chars> encrypts agent keys + card secrets at rest
# GLASSPAY_ADMIN_TOKEN=<random token> protects the management API
bun dev # server on :4070
bun run --cwd packages/dashboard dev # dashboard on :4071Issue a card from the dashboard (Privy login), or via the admin API:
curl -X POST localhost:4070/api/cards \
-H "Authorization: Bearer $GLASSPAY_ADMIN_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"my agent card","terms":{"pay":{"period":{"amount":"5","seconds":604800}}}}'
# -> { "card_id": ..., "card_url": "http://localhost:4070/c/<secret>/mcp" }Plug the card_url into an agent and it can spend.
bun test # engine + server suites
bun run typecheck # per-package tsc| Var | Required | Purpose |
|---|---|---|
GLASSPAY_MASTER_KEY |
yes | 32-byte hex key; encrypts agent keys and card secrets at rest |
GLASSPAY_ADMIN_TOKEN |
yes | ops bearer token for the management API (/api/*): full access, server-side scripts only, never shipped to a browser |
GLASSPAY_PRIVY_APP_ID |
dashboard lane | enables per-user API auth: Privy access tokens verified offline against the app's JWKS; every route scoped to the authenticated user |
PORT |
no | server port (default 4070) |
GLASSPAY_DB_PATH |
no | SQLite path (default .dev/remit.sqlite) |
GLASSPAY_RPC_URL |
no | Base RPC (default https://mainnet.base.org) |
GLASSPAY_PUBLIC_MCP_BASE |
prod | public origin used when rendering card URLs (unset = localhost; also arms the MCP Host allowlist) |
GLASSPAY_ALLOWED_HOSTS |
no | extra Host headers accepted on the MCP endpoint (comma-separated; e.g. a platform fallback domain) |
GLASSPAY_CORS_ORIGINS |
no | comma-separated allowed origins for the API |
GLASSPAY_DEV_USER_PK |
no | dev-only server-custodied user key (server-signed issuance lane) |
GLASSPAY_FACILITATOR_BASE |
no | x402 facilitator base URL (defaults to self) |
GLASSPAY_SELLER_PAYTO |
no | payout address for the built-in demo seller |
GLASSPAY_PAID_FETCH_ALLOW_LOCAL |
no | allow paid_fetch to hit local/private hosts (dev only) |
GLASSPAY_STRIPE_WEBHOOK_SECRET |
no | Stripe real-time auth webhook signing secret (test mode); unset = the fiat leg answers 503 (disabled) |
STRIPE_SECRET_KEY |
no | Stripe TEST-mode secret key (sk_test_/rk_test_ only; anything else is refused); enables fiat_pay, card_credentials, and the demo shop |
GLASSPAY_FIAT_SETTLEMENT |
no | 1 = approved Visa charges settle on-chain as real delegated USDC transfers (see GLASSPAY_SETTLEMENT_ADDRESS, GLASSPAY_FIAT_FEE_HEADROOM, GLASSPAY_FIAT_SETTLE_INTERVAL_MS) |
GLASSPAY_SETTLEMENT_ADDRESS |
settlement | recipient of the fiat settlement transfers (validated at boot; default = the fee collector) |
VENICE_API_KEY |
no | enables POST /cards/compile (plain-language card drafting); unset = the compile endpoint refuses (disabled) |
VENICE_MODEL |
with key | Venice model id for the NL compiler; pin it (the fallback default is unvalidated) |
VENICE_BASE_URL |
no | Venice API base override (defaults to the public Venice endpoint) |
BASESCAN_API_KEY |
no | enables verified-contract labels from Basescan when resolving compiled drafts |
GLASSPAY_DASHBOARD_BASE |
OAuth lane | dashboard origin that hosts the OAuth consent (card-picker) page (default http://localhost:4071) |
GLASSPAY_RECONCILE_INTERVAL_MS |
no | stuck-pending-charge reconcile sweep interval (default 300000; 0 disables) |
GLASSPAY_MCP_RATE_LIMIT / GLASSPAY_MCP_BAD_SECRET_LIMIT |
no | per-card and per-IP-bad-secret request ceilings per minute (defaults 240 / 30) |
GLASSPAY_OAUTH_ACCESS_TTL / GLASSPAY_OAUTH_REFRESH_TTL |
no | OAuth access / refresh token lifetimes in seconds (defaults 3600 / 2592000) |
GLASSPAY_OAUTH_REDIRECT_HOSTS |
no | if set, restricts OAuth https redirect-URI hosts to this allowlist (loopback + custom schemes always allowed; recommended in prod) |
GLASSPAY_OAUTH_ACCEPTED_RESOURCES |
no | extra RFC 8707 resource URIs still honored (legacy values during a base-URL migration) |
GLASSPAY_TRUST_PROXY_HOPS |
no | trusted proxy hops for client-IP rate limiting (default 1 = Railway edge; 0 disables XFF trust) |
NEXT_PUBLIC_PRIVY_APP_ID / NEXT_PUBLIC_PRIVY_CLIENT_ID |
dashboard | Privy app credentials (public identifiers, not secrets) |
NEXT_PUBLIC_GLASSPAY_API |
dashboard | server API base, e.g. http://localhost:4070/api |
NEXT_PUBLIC_BASE_RPC |
dashboard | Base RPC for client-side reads |
The dashboard carries no shared secret: every API call sends the signed-in user's Privy session token, which the server verifies and scopes. The deployed dashboard origin must be listed in the server's GLASSPAY_CORS_ORIGINS.
OpenTelemetry / SigNoz variables (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_TRACES_EXPORTER, OTEL_METRICS_EXPORTER, OTEL_LOGS_EXPORTER, GLASSPAY_OTEL_DEBUG) are documented in .env.example.
- Custody: your funds stay in your wallet. The per-card agent key signs redelegations only; it holds no assets and is encrypted at rest. You can export your wallet's private key from the account menu at any time (through Privy's secure modal, rendered in a separate-domain iframe remit never reads) and walk away to any client.
- Dashboard auth: per-user Privy sessions, verified server-side against the app JWKS. At onboard, the embedded wallet signs
glasspay-onboard:v1:<did>to prove key possession bound to that login; from then on, every card route is scoped to the authenticated user's own cards. - Issuance integrity: the server verifies the delegation signature recovers to the delegator on both issuance lanes before persisting a card.
- Card secrets: 256-bit, stored as a hash for auth and AES-256-GCM-encrypted at rest for the reveal/rotate feature; the URL is a credential, rotate it like a password.
- Limits enforced twice: server-side at call time (typed refusals) and on-chain by caveat enforcers at redemption. Period, lifetime, expiry, usage count, and contract target/method have dedicated on-chain enforcers; the per-transaction max and merchant allowlist are server-side carve policy, backstopped on-chain by the leaf's amount scope.
- Revocation layers: freeze (server, reversible) -> revoke (card + subtree, permanent) -> nuke (on-chain nonce bump, kills every delegation ever issued by the wallet). All three are user-operable from the dashboard; on-chain revoke and nuke are signed by the user's own embedded wallet in the browser (an admin leaf delegation) and ride the relayer gaslessly.
- MCP surface hardening: Host allowlist (DNS-rebinding guard), per-card and bad-secret rate limits, 1 MiB body cap, secrets never echoed in errors or logs.
- Stripe leg: test mode only, by design; the real-time auth webhook answers from cached delegation state within Stripe's 2s window. With settlement enabled, an approved charge settles as a real delegated USDC transfer afterwards (the same enforcers count both rails), and a charge whose settlement cannot land parks
settlement_unconfirmedand freezes the card rather than ever releasing its budget.
/shop (also served at https://shop.s0nderlabs.xyz) is a small storefront, "s0nder supply co.", that accepts the cards' Visas. It exists to show the fiat lane end to end with nothing mocked on our side of the rail:
- An agent asks its card for credentials (
card_credentials) and fills the checkout form like it would at any web store. - The shop fires a real Stripe test-mode authorization; Stripe calls our real-time auth webhook; the webhook answers approve/decline from the card's on-chain delegation state within Stripe's 2-second window.
- A decline (e.g. an item over the card's weekly budget) comes back typed, from the card's terms, not from the merchant.
- With settlement enabled, the approved charge settles as a real delegated USDC transfer on Base, through the same enforcers that meter the crypto rail. One budget, two rails.
Catalog prices are all $5 or less because approved purchases move real USDC.
| Document | Contents |
|---|---|
| docs/architecture.md | Full system architecture with all 16 SigNoz use cases (traces, metrics, logs, dashboards, alerts, saved views, cost control, service map, SigNoz MCP) |
| docs/signoz-verification.md | Step-by-step guide to verify every SigNoz feature in the live deployment |
| docs/blog-post.md | The observability story: instrumenting agentic payments with OpenTelemetry + SigNoz |
| docs/medium-post.md | Medium-ready version of the observability story with screenshot placeholders (IMG-1..IMG-13) |
| docs/screenshots/README.md | Which screenshot goes where: drop files as img-01.png..img-13.png |
| docs/video-script.md | Demo video script (3 minutes) |
| CHANGELOG.md | Release history |
MIT. See LICENSE.
