dgb-tollgate
v0.2.0
Published
DigiDollar paywall proxy for Streamable-HTTP MCP servers (x402 at the MCP layer)
Downloads
123
Readme
dgb-tollgate
DigiDollar paywall proxy for MCP servers — plus an autonomous paying client.
v0.1 is the paywall proxy (src/server.js): it returns HTTP 402 with a
DigiDollar invoice and issues bearer tokens after payment. v0.2 is the paying
client (src/client.js): a stdio MCP server an AI agent connects to; it
detects 402, pays DigiDollar automatically (within spend caps), obtains a token,
and mirrors upstream tools — no human in the loop.
TESTNET ONLY — not audited
This is v0.1 research / demo software. Default
REQUIRE_TESTNET=truerefuses to run against a non-test chain. Do not use on mainnet with real funds. There is no security audit. Expect breaking changes. You run this at your own risk.
Flow (v0.1 tollgate proxy)
Agent dgb-tollgate Your node / upstream
| | |
|-- MCP request (no token) ----->| |
|<- 402 + fresh DD invoice ------| |
| |-- getdigidollaraddress ---------->|
| | |
|-- pay DigiDollar to payTo --------------------------------------> chain
| | |
|-- POST /token { invoiceId } -->| |
| |-- listdigidollarutxos (own node) ->|
|<- { token } -------------------|(only if address balance ≥ price) |
| | |
|-- MCP + Authorization: Bearer >| |
| |-- reverse-proxy ----------------->| upstream MCP
|<- upstream response -----------|<- streamed response --------------|Flow (v0.2 autonomous loop)
Claude / agent paying client (stdio) tollgate (v0.1) upstream MCP
| | | |
|-- MCP stdio ------------------>| | |
| |-- HTTP MCP (no token) ------>| |
| |<- 402 + DD invoice ----------| |
| |-- senddigidollar (wallet) ---> node |
| |-- POST /token -------------->| |
| |<- bearer token ---------------| |
| |-- HTTP MCP + Bearer -------->|-- proxy ------------->|
|<- tool result ------------------|<- upstream response ----------|<-----------------------|The paying client caches the bearer token until expiry. It only pays again when
there is no valid token. Spend caps (DD_MAX_PAYMENT_USD, DD_MAX_DAILY_USD) are
enforced before any senddigidollar call.
- Unpaid request →
HTTP 402with a fresh DigiDollar address and invoice id. - Agent pays that address in DigiDollar (minimum $1.00 — consensus floor).
POST /tokenwith{ "invoiceId" }→ tollgate checks its own node (listdigidollarutxosat the invoice address). Client txids are never trusted.- On success, a short-lived HMAC-SHA256 bearer token is issued.
- Requests with a valid Bearer token are reverse-proxied to
UPSTREAM_MCP_URL(method, headers, body; streamed response).
Quick start
npm install
# Configure (placeholders only in the repo — use your own secrets locally)
cp .env.example .env # then edit; do not commit .env
# Terminal A — example upstream MCP (one tool: echo)
npm run upstream
# Terminal B — tollgate (loads env from your shell / dotenv of your choice)
export $(grep -v '^#' .env | xargs) # or use your preferred env loader
npm start
# Terminal C — v0.1 walkthrough (payment step is manual)
npm run demo
# Or: v0.2 autonomous loop (payment is automatic)
npm run paid-demoPaying client (v0.2)
Point your MCP host at the stdio server (bin: dgb-tollgate-client):
{
"mcpServers": {
"dgb-tollgate-client": {
"command": "node",
"args": ["/path/to/dgb-tollgate/src/client.js"],
"env": {
"TOLLGATE_URL": "http://127.0.0.1:8080/mcp",
"TOLLGATE_TOKEN_URL": "http://127.0.0.1:8080/token",
"DGB_RPC_URL": "http://127.0.0.1:14023",
"DGB_RPC_USER": "rpcuser",
"DGB_RPC_PASSWORD": "…",
"DGB_WALLET": "wallet-name",
"DD_MAX_PAYMENT_USD": "5",
"DD_MAX_DAILY_USD": "25"
}
}
}
}Or: npm run client (with the same env vars exported in your shell).
Config
Tollgate server (v0.1)
| Variable | Required | Default | Description |
|---|---|---|---|
| UPSTREAM_MCP_URL | yes | — | Streamable-HTTP MCP endpoint to protect |
| PRICE_USD | yes | — | Price per token; must be ≥ 1.00 |
| TOKEN_TTL_SECONDS | no | 3600 | Bearer token lifetime after payment |
| TOLLGATE_SECRET | yes | — | HMAC signing secret (≥ 16 chars) |
| DGB_RPC_URL | yes | — | DigiByte Core RPC URL |
| DGB_RPC_USER | yes | — | RPC username |
| DGB_RPC_PASSWORD | yes | — | RPC password |
| DGB_WALLET | yes | — | Wallet name (fresh DD addresses) |
| REQUIRE_TESTNET | no | true | Exit unless node chain is test |
| PORT | no | 8080 | Tollgate listen port |
| INVOICE_TTL_SECONDS | no | 900 | How long an unpaid invoice is valid |
| MIN_CONFIRMATIONS | no | 1 | Confirmations required for payment |
| MAX_OUTSTANDING_INVOICES | no | 1000 | Cap on unsettled invoices; excess requests get HTTP 429 |
Startup rejects PRICE_USD < 1.00 because DigiDollar cannot create outputs
below $1.00.
Paying client (v0.2)
| Variable | Required | Default | Description |
|---|---|---|---|
| TOLLGATE_URL | yes | — | Paywalled Streamable-HTTP MCP endpoint |
| TOLLGATE_TOKEN_URL | yes | — | Tollgate POST /token URL |
| DGB_RPC_URL | yes | — | DigiByte Core RPC (wallet that pays) |
| DGB_RPC_USER | yes | — | RPC username |
| DGB_RPC_PASSWORD | yes | — | RPC password |
| DGB_WALLET | yes | — | Wallet name used for senddigidollar |
| DD_MAX_PAYMENT_USD | no | 5 | Per-payment spend cap |
| DD_MAX_DAILY_USD | no | 25 | Rolling 24h spend cap |
| REQUIRE_TESTNET | no | true | Exit unless node chain is test |
| PAY_POLL_TIMEOUT_MS | no | 90000 | Max wait for /token after payment |
| PAY_POLL_INTERVAL_MS | no | 3000 | Poll interval for /token |
Security model (do not weaken)
- Payment is verified only by querying your DigiByte node for DigiDollar UTXOs at the invoice address. A client-supplied txid is never proof.
- Fresh DigiDollar address per invoice so “paid?” = address balance lookup.
- Bearer tokens are HMAC-SHA256 over
{ invoiceId, exp }; verification uses a constant-time compare. - Secrets and tokens are never logged.
- Testnet guard is on by default.
- The paying client enforces spend caps before payment (allowance, not custody).
senddigidollaralways receives integer cents (never decimal dollars).
API surface
| Method | Path | Auth | Behavior |
|---|---|---|---|
| POST | /token | none | Body { invoiceId } → verify payment → { token, tokenType, expiresAt, invoiceId } |
| * | any other | Bearer | Valid token → proxy to upstream; else 402 + new invoice |
Example 402 body
{
"x402Version": 1,
"error": "Payment Required",
"accepts": [
{
"scheme": "exact",
"network": "digibyte-testnet",
"asset": "DigiDollar",
"payTo": "<fresh-dd-address>",
"amountUsd": 1,
"amountCents": 100,
"invoiceId": "<uuid>",
"expiresAt": "2026-01-01T00:15:00.000Z"
}
],
"hint": "Pay the DigiDollar amount to payTo, then POST /token ..."
}Layout
src/server.js v0.1 tollgate proxy (Express)
src/client.js v0.2 paying MCP client (stdio)
src/payer.js DigiDollar payment + spend-cap policy
example/upstream-mcp.js Minimal Streamable-HTTP MCP (tool: echo)
example/demo.sh v0.1: manual payment walkthrough
example/paid-agent-demo.sh v0.2: fully autonomous payment demo
reference/ Proven modules from dgb-x402 (source of security model)Non-goals (v0.1 / v0.2)
- Per-tool pricing (all-or-nothing per token)
- Digi-ID identity gating
- Batch / metered settlement
- stdio→HTTP bridging (upstream must already be HTTP)
- Multi-tenant dashboards or durable persistence
License
MIT
