x402-k8s-operator

Kubernetes operator that monetizes any API with per-request payments via the x402 protocol.
Single binary, single Deployment. Patches your existing Ingress to enforce payment on specified paths. Works with any Ingress controller (NGINX, Traefik, etc.) using only the standard networking.k8s.io/v1 Ingress API.
Internet
|
v
+----------------------------+
| Your existing Ingress | <-- operator patches this
| (NGINX / Traefik / etc) |
+------+-----------+---------+
| |
paid paths free paths
| |
v v
+-----------------+ +-----------------+
| x402-k8s- | | Backend Service |
| operator :8402 | | (your API) |
| (payment check) | +-----------------+
+--------+--------+
|
402 if no payment
forward if paid
|
v
+-----------------+
| Backend Service |
| (your API) |
+-----------------+
Quick Start
1. Install the operator
kubectl (single manifest):
kubectl apply -f https://raw.githubusercontent.com/razvanmacovei/x402-k8s-operator/main/install.yaml
Helm:
helm install x402-k8s-operator oci://ghcr.io/razvanmacovei/charts/x402-k8s-operator
2. Create an X402Route
Assuming you already have an Ingress called my-api-ingress:
apiVersion: x402.io/v1alpha1
kind: X402Route
metadata:
name: my-api-payments
spec:
ingressRef:
name: my-api-ingress
payment:
wallet: "0xYourWalletAddress"
network: base-sepolia
defaultPrice: "0.001"
routes:
- path: "/api/*"
# inherits defaultPrice, all requests pay
- path: "/health"
free: true
- path: "/docs/**"
free: true
That's it. The operator automatically:
- Compiles route rules into an in-memory store
- Patches your Ingress: paid paths -> operator service, free paths -> original backend
- Serves traffic on port 8402: checks payment -> verifies with facilitator -> proxies to backend
CRD Reference
Example: Per-path pricing with conditional mode
apiVersion: x402.io/v1alpha1
kind: X402Route
metadata:
name: my-api
spec:
ingressRef:
name: my-ingress
payment:
wallet: "0x..."
network: base-sepolia
defaultPrice: "0.001"
routes:
- path: "/api/v1/*"
# inherits defaultPrice, mode defaults to all-pay
- path: "/api/v2/**"
price: "0.01"
mode: conditional
conditions:
- header: "X-Bot-Score"
pattern: "^(bot|automated)$"
action: pay
- header: "User-Agent"
pattern: "(?i)(claude|openai|anthropic)"
action: pay
- path: "/health"
free: true
- path: "/docs/**"
free: true
Spec Fields
| Field |
Type |
Required |
Description |
ingressRef.name |
string |
yes |
Name of the existing Ingress to patch |
ingressRef.namespace |
string |
no |
Namespace of the Ingress (defaults to X402Route's ns) |
payment.wallet |
string |
yes |
Wallet address to receive payments |
payment.network |
string |
yes |
Blockchain network (see Networks table) |
payment.defaultPrice |
string |
no |
Default price for paid routes (e.g. "0.001") |
payment.facilitatorURL |
string |
no |
Facilitator URL (defaults to https://x402.org/facilitator) |
routes[].path |
string |
yes |
Path pattern (* = one segment, ** = any depth) |
routes[].price |
string |
no |
Price override for this path |
routes[].free |
bool |
no |
Mark path as free |
routes[].mode |
string |
no |
all-pay (default) or conditional |
routes[].conditions[] |
array |
no |
Conditions for conditional mode |
routes[].conditions[].header |
string |
yes |
HTTP header to inspect |
routes[].conditions[].pattern |
string |
yes |
Regex pattern to match |
routes[].conditions[].action |
string |
yes |
pay or free when matched |
Status Fields
| Field |
Type |
Description |
status.ingressPatched |
bool |
Whether the Ingress has been patched |
status.ready |
bool |
Whether the route is fully active |
status.activeRoutes |
int |
Number of active route rules |
status.conditions |
[]Condition |
Standard Kubernetes conditions |
Architecture
Single pod runs both controller and HTTP proxy as goroutines:
| Port |
Purpose |
:8080 |
/metrics (Prometheus) |
:8081 |
/healthz, /readyz (probes) |
:8402 |
Gateway proxy (traffic) |
The controller watches X402Route CRDs and writes compiled routes to an in-memory store. The gateway reads from the store instantly — no ConfigMap polling, no separate Deployment.
Traffic Flow
Client -> Ingress Controller -> x402-k8s-operator :8402 -> payment check -> Original Backend
Payment Protocol (x402)
Implements the x402 specification, compatible with the official Coinbase CDP facilitator.
- Request:
Payment-Signature header (Base64-encoded JSON payload; falls back to X-Payment for compat)
- 402 Response:
PAYMENT-REQUIRED header (Base64-encoded JSON) + JSON body (resource object, amount in atomic units, extra asset metadata)
- 200 Response:
PAYMENT-RESPONSE header (Base64-encoded JSON with transaction hash, network, payer)
- Facilitator flow: Gateway POSTs
{paymentPayload, paymentRequirements} to /verify, then /settle on success
Prometheus Metrics
| Metric |
Type |
Description |
x402_requests_total |
counter |
Requests by path, namespace, route, payment status |
x402_payment_amount_total |
counter |
Payment amounts by path, wallet, network |
x402_payment_verification_duration_seconds |
histogram |
Facilitator verification latency |
x402_proxy_request_duration_seconds |
histogram |
Backend proxy latency |
x402_active_routes |
gauge |
Number of active routes |
x402_route_store_updates_total |
counter |
Route store update count |
Grafana Dashboard

Import the pre-built dashboard from Grafana.com (ID: 24921) or auto-provision it via Helm:
grafanaDashboard:
enabled: true
namespace: monitoring # namespace where Grafana is deployed
The dashboard includes: request rate by status, revenue tracking, payment verification latency (p50/p95/p99), proxy latency, route table, and pod health.
Local Development
Prerequisites: Go 1.25+, Docker, a local Kubernetes cluster (docker-desktop, Kind, or Minikube).
# Build the manager binary
make build
# Build Docker image
make docker-build
# Deploy to local cluster
make deploy-local
# Apply sample X402Route
make sample
# Remove everything
make undeploy
Testing
With mock facilitator (no blockchain needed)
# Terminal 1: Start a simple backend
python3 -m http.server 9090
# Terminal 2: Start the mock facilitator
go run ./cmd/mock-facilitator/
# Terminal 3: Run the test client
go run ./cmd/test-client/ http://localhost:8402/api/hello
The test client sends two requests:
- Without payment -> expects
402 Payment Required
- With a mock
Payment-Signature header -> expects 200 OK
Production
For production, use a mainnet network with a real USDC wallet:
payment:
wallet: "0xYourProductionWallet"
network: base
defaultPrice: "0.01"
Networks
| Network |
Chain ID |
USDC Contract |
base |
eip155:8453 |
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
base-sepolia |
eip155:84532 |
0x036CbD53842c5426634e7929541eC2318f3dCF7e |
avalanche |
eip155:43114 |
0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E |
avalanche-fuji |
eip155:43113 |
0x5425890298aed601595a70AB815c96711a31Bc65 |
solana |
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp |
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
solana-devnet |
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 |
4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU |
Prices are human-readable (e.g. "0.001" USDC) and automatically converted to atomic units.
Contributing
Contributions are welcome! See CONTRIBUTING.md for guidelines.
Please read our Code of Conduct before participating.
Security
For reporting security vulnerabilities, see SECURITY.md.
License
Apache License 2.0