The x402 gateway is an HTTP layer in front of an application server that:
- challenges unpaid requests with HTTP
402 Payment Required - verifies payment using Stackflow APIs
- forwards the request upstream only after payment verification succeeds
The gateway currently supports two payment modes:
direct: immediate verification from the requestor proofindirect: wait for a forwarded payment record and validate a reveal secret
Implementation entrypoint: server/src/x402-gateway.ts
Current scaffold scope:
- one protected path (
STACKFLOW_X402_PROTECTED_PATH) - one in-memory replay set keyed by
(method, path+query, proof payload hash) - direct verification using
POST /counterparty/transfer - indirect verification using:
GET /forwarding/payments?paymentId=<id>POST /forwarding/reveal
Runtime components:
- client (payer)
- x402 gateway (public ingress)
- stackflow-node (private/internal service)
- upstream application server (private/internal service)
- optional next-hop stackflow-node(s) for routed payments
Data/control flow:
- client requests protected resource
- gateway checks
x-x402-payment - gateway verifies payment with stackflow-node
- gateway marks proof as consumed (TTL window)
- gateway proxies request to upstream app with verification headers
Protected route: request path must equal STACKFLOW_X402_PROTECTED_PATH.
Payment proof transport:
- header:
x-x402-payment - format: JSON string or base64url-encoded JSON
On missing/invalid proof, gateway returns:
- status
402 WWW-Authenticate: X402 ...- machine-readable JSON payload describing accepted modes and fields
Accepted proof shape:
mode: "direct"(optional; if omitted, payload is treated as direct)- direct transfer proof fields (
action = 1) compatible withPOST /counterparty/transfer
Verification steps:
- parse and validate fields (
amount, balances, nonce, signatures, etc.) - enforce
amount >= STACKFLOW_X402_PRICE_AMOUNT - call
POST /counterparty/transferon stackflow-node with peer headers - require stackflow response
2xxandok: true - proxy upstream if accepted
Accepted proof shape:
mode: "indirect"paymentIdsecret(32-byte hex preimage)expectedFromPrincipal
Verification steps:
- poll
GET /forwarding/payments?paymentId=...until timeout - require payment exists and
status = completed - require forwarding metadata indicates payer principal matches
expectedFromPrincipal - require payment includes
hashedSecret - call
POST /forwarding/revealwith{ paymentId, secret } - require reveal response
2xxandok: true - proxy upstream if accepted
For verified requests, gateway forwards all non-hop-by-hop headers and adds:
x-stackflow-x402-verified: truex-stackflow-x402-proof-hash: <sha256>
For unprotected routes, gateway proxies without requiring payment.
Replay defense is currently in-memory and process-local:
- replay key = hash of method + path/query + normalized proof payload
- consumed key retained for
STACKFLOW_X402_PROOF_REPLAY_TTL_MS - a replayed key returns
402withpayment-proof-already-used
Implications:
- restart clears consumed proof memory
- multi-instance deployments do not share replay state by default
Core:
STACKFLOW_X402_GATEWAY_HOST(default127.0.0.1)STACKFLOW_X402_GATEWAY_PORT(default8790)STACKFLOW_X402_UPSTREAM_BASE_URL(defaulthttp://127.0.0.1:3000)STACKFLOW_X402_STACKFLOW_NODE_BASE_URL(defaulthttp://127.0.0.1:8787)STACKFLOW_X402_PROTECTED_PATH(default/paid-content)STACKFLOW_X402_PRICE_AMOUNT(default1000)STACKFLOW_X402_PRICE_ASSET(defaultSTX)
Timeouts and polling:
STACKFLOW_X402_STACKFLOW_TIMEOUT_MS(default10000)STACKFLOW_X402_UPSTREAM_TIMEOUT_MS(default10000)STACKFLOW_X402_PROOF_REPLAY_TTL_MS(default86400000)STACKFLOW_X402_INDIRECT_WAIT_TIMEOUT_MS(default30000)STACKFLOW_X402_INDIRECT_POLL_INTERVAL_MS(default1000)
Indirect read auth:
STACKFLOW_X402_STACKFLOW_ADMIN_READ_TOKEN(optional)- fallback:
STACKFLOW_NODE_ADMIN_READ_TOKEN
Recommended:
- expose only the gateway publicly
- keep stackflow-node on private network or localhost-only bind
- keep upstream app private behind gateway
- keep observer endpoints restricted to trusted sources/localhost
- terminate TLS at ingress or run end-to-end TLS/mTLS
- apply standard edge protections: WAF, rate limits, request size limits
- if behind trusted proxy chain, configure stackflow-node proxy trust carefully
- payment callers interact with gateway only
- do not expose stackflow-node admin/sensitive endpoints directly
- when indirect mode is used and admin-read token is set, pass token only from gateway to stackflow-node over trusted internal network
Single instance is simplest and currently recommended.
For multi-instance gateway:
- move replay state from memory to shared durable store (Redis/DB)
- use deterministic idempotency keys across replicas
- ensure consistent route pricing policy across replicas
Define explicit external behavior for:
- stackflow-node timeout/unavailable -> return
402with reason - indirect payment wait timeout -> return
402with timeout reason - upstream timeout/unavailable after payment accept -> return retriable
5xx
- Treat all payment proof inputs as untrusted.
- Validate mode-specific schema before any downstream call.
- Keep stackflow-node forwarding restrictions enabled (allowed base URLs, private-destination policy).
- Do not log raw secrets or signatures in plaintext in production logs.
- Bound request body size and header size at ingress.
- Run gateway and stackflow-node with least-privilege OS/container permissions.
Recommended telemetry:
- counters:
x402_challenge_totalx402_direct_accept_totalx402_indirect_accept_totalx402_reject_total{reason=...}
- latency histograms:
- direct verification latency
- indirect wait duration
- upstream proxy latency
- gauges:
- in-memory replay set size
Recommended structured log fields:
request_idmode(direct|indirect)proof_hashpayment_id(indirect)decision(challenged|accepted|rejected)reason
Current limitations:
- one protected path instead of route policy table
- no persistent/shared replay store
- no dynamic pricing policy per route/method/tenant
- no settlement finality policy layer beyond stackflow-node acceptance
Planned improvements:
- route policy config map (
method+path -> price/asset/mode policy) - shared replay/idempotency backend for HA
- richer indirect payer attestation model beyond principal equality
- metrics and structured logging integration
- integration tests for gateway-specific negative cases and chaos scenarios