o402 0.1.4

OpenAI-compatible gateway, paid with x402.
docs.rs failed to build o402-0.1.4
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: o402-0.1.0

O402

Crates.io Docs.rs CI License Rust

OpenAI-compatible HTTP gateway, paid with x402.

One binary, one TOML file. Clients speak the OpenAI HTTP API (base_url; dummy Authorization is stripped). Payment is x402 v2, not an API key. TLS terminates at a reverse proxy. o402 holds no seller private keys — only pay-to addresses and upstream LLM keys. Run a remote facilitator for verify/settle.

Requires Rust 1.95. Default features: evm + svm. Image stays default features (no full). No OpenTelemetry.

Install

macOS / Linux

curl -fsSL https://sh.qntx.org/o402 | sh

Windows (PowerShell)

irm https://sh.qntx.org/o402/ps | iex

Or via Cargo:

cargo install o402
# from this tree: cargo install --path crates/o402

Run

o402 init --path gateway.toml
# edit gateway.toml; for payment copy deploy/gateway.example.toml
o402 serve --config gateway.toml

serve --config is required. Bind comes from server.bind (default 0.0.0.0:8080 for containers). o402 init writes the unpaid example. There is no sync command.

CLI: o402 init [--path gateway.toml] and o402 serve --config PATH.

HTTP

Method Path Auth
GET /health unpaid — liveness { "status": "ok", "service": "o402", "version": "…" }
GET /ready unpaid — 200 when payment is off, or when facilitator GET /supported succeeds; 503 otherwise
GET /v1/models and /openai/v1/models unpaid — proxied to Bifrost
GET /v1/pricing unpaid — o402 rates
POST /v1/chat/completions, /v1/completions, /v1/embeddings, /v1/responses (and /openai prefixed) paid upto (token usage)
other exported HTTP /v1/*, /openai/*, /anthropic/*, /genai/*, /bedrock/*, /cohere/*, /litellm/*, /langchain/*, /pydanticai/* paid exact (flat price)
GET /v1/realtime, Upgrade: websocket, /api/*, / 404 — cannot bill

Unknown models are forwarded. Catalog [[models]] is optional (rate/upstream overrides only).

Point an OpenAI SDK at server.base_url (/v1/...) or server.base_url/openai (Bifrost drop-in). Dummy Authorization is stripped; the gateway injects the upstream key.

Config

TOML with deny_unknown_fields. After parse, a string is replaced only when the entire value is $VAR or ${VAR}. "Bearer $TOKEN" is not expanded. Auth header values are the full header ($FACILITATOR_AUTH_HEADER = Bearer …). dotenvy loads .env from the process cwd at start.

Examples: deploy/gateway.example.toml (paid EVM+SVM), deploy/gateway.full.example.toml (production extras; not used by Docker), and deploy/gateway.unpaid.example.toml (payment.enabled = false). Env names: deploy/o402.env.example.

server.base_url is required when payment is on (r402 never derives origin from Host). Loopback http is allowed; public URLs must be https.

Payment

Cargo features match the facilitator production matrix. Default binary is EVM+SVM. Rebuild with extras (and a facilitator built with the same extras) to advertise those namespaces.

cargo install --path crates/o402 --locked --features full
Feature Namespace Schemes Named asset
evm (default) eip155 exact, upto usdc
svm (default) solana exact usdc
near near exact usdc
xrpl xrpl exact xrp, rlusd
hedera hedera exact usdc, hbar
avm algorand exact usdc
aptos aptos exact usdc
keeta keeta exact usdc
tvm tvm exact usdt
stellar stellar exact usdc
concordium ccd exact usdr, ccd
experimental-tron tron exact usdt

full = production extras minus experimental-tron. No casper. SVM upto stays out (voucher signer / seller key). o402 holds no seller keys.

TOML price values are asset units, not a USD peg. shared_decimals is a scale check: mixing Stellar USDC (7), HBAR (8), or RLUSD (15) with USDC-6 fails startup. Same-decimal mixes (USDC-6 + XRP-6) load; treating those rates as dollars is economically inconsistent.

[payment.facilitator] url is the remote facilitator (POST /verify, POST /settle, GET /supported). Public facilitator.qntx.org requires path-keyed auth (entire header value):

[payment.facilitator]
url = "https://facilitator.qntx.org"
timeout_secs = 60

[payment.facilitator.auth.verify]
Authorization = "$FACILITATOR_AUTH_HEADER"

[server.cors] origins must list every SPA origin (https://chat.qntx.org, Vite http://localhost:5173). Empty origins = no CORS layer; the browser then hides Payment-Required / Payment-Response.

Metrics

Default: off. Optional observability.metrics_bind = "127.0.0.1:9090" serves GET /metrics on that bind only. It is never mounted on the payment port (:8080). No OpenTelemetry.

Docker Compose

Gateway HTTP 8080 is loopback/internal. Caddy publishes 80/443 and terminates TLS. Reverse proxy uses flush_interval -1 so SSE is not buffered.

cp deploy/gateway.example.toml deploy/gateway.toml
cp deploy/o402.env.example deploy/o402.env
cp deploy/bifrost-config.example.json deploy/bifrost-config.json
# fill o402.env (pay-to, OPENAI_API_KEY, FACILITATOR_AUTH_HEADER); set server.base_url / Caddyfile hostname
docker compose -f deploy/docker-compose.yml up --build -d

Bifrost admin UI: http://127.0.0.1:8081 (loopback). Public traffic hits Caddy → o402 → Bifrost.

Image: pin ghcr.io/qntx/o402:0.1.4 (tag v* publishes GHCR; PRs only build). Default features only (evm+svm). HEALTHCHECK is GET /health. Official caddy:2 — no rate-limit plugin. SSE requires flush_interval -1 in the Caddyfile. linux/amd64 only.

docker build -t o402 .
docker run --rm -p 127.0.0.1:8080:8080 --env-file deploy/o402.env \
  -v "$PWD/deploy/gateway.toml:/app/gateway.toml:ro" o402

systemd

Host + reverse proxy: set server.bind = "127.0.0.1:8080" and terminate TLS at Caddy (change deploy/Caddyfile o402:8080 to 127.0.0.1:8080). The container keeps 0.0.0.0:8080 so Compose Caddy can reach it on the Docker network.

sudo useradd --system --home /etc/o402 --shell /usr/sbin/nologin o402
sudo mkdir -p /etc/o402
sudo cp gateway.toml /etc/o402/gateway.toml
sudo cp o402.env /etc/o402/o402.env
sudo chown -R o402:o402 /etc/o402
sudo chmod 640 /etc/o402/o402.env
sudo install -m 755 target/release/o402 /usr/bin/o402
sudo install -m 644 deploy/o402.service /etc/systemd/system/o402.service
sudo systemctl daemon-reload
sudo systemctl enable --now o402

Unit: EnvironmentFile=-/etc/o402/o402.env, ExecStart=/usr/bin/o402 serve --config /etc/o402/gateway.toml, Restart=on-failure. TimeoutStopSec=65s covers HTTP drain plus inflight settle (2 × server.shutdown_timeout_secs default 30, plus slack).

Crates

See crates/README.md for the crate table.

Contributing

See CONTRIBUTING.md for development setup, PR expectations, and the release checklist.

Security

This library has not been independently audited. Use at your own risk.

See SECURITY.md for scope and reporting.

License

Licensed under either of:

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project shall be dual-licensed as above, without any additional terms or conditions.


A QuantX open-source project.

Code is law. We write both.