gelu22/x402-gateway-omarchy:为 AI 智能体自动支付 x402 付费墙的 Omarchy 组件
gelu22/x402-gateway-omarchy
gelu22/x402-gateway-omarchy 发布 v0.1.27,通过本地 Go 守护进程和 MCP/unix socket,让 AI 智能体用 USDC 自动支付 x402 付费墙,并受每日预算上限约束。
Pays x402 paywalls in USDC for your AI agents, within a daily budget you set — bar widget plus a local daemon for Omarchy.
Install
Download the installer from a pinned release, verify its signature, then run it — never pipe a remote script straight into a shell:
VERSION=v0.1.27 # any released tag (see the Releases page) TMP="$(mktemp -d)" curl -fsSL -o "$TMP/install.sh" \ "https://github.com/gelu22/x402-gateway-omarchy/releases/download/$VERSION/install.sh" \ && gh attestation verify "$TMP/install.sh" --repo gelu22/x402-gateway-omarchy \ --signer-workflow gelu22/x402-gateway-omarchy/.github/workflows/release.yml \ --source-ref "refs/tags/$VERSION" \ && bash "$TMP/install.sh" "$VERSION"
Update = the same chain with a newer tag (see CHANGELOG.md).
Uninstall = download+verify uninstall.sh the same way, then
bash "$TMP/uninstall.sh" (or --yes for a full wipe). Non-destructive:
install.sh remove keeps state/config. Wallets stay in the CDP project.
The installer checks the daemon binary and plugin bundle (sha256 and
sigstore attestation via gh). Offline / at your own risk:
GATEWAY_ALLOW_UNVERIFIED=1. Explicit tag required — no floating latest.
AI agent ──► MCP / unix socket ──► Go daemon ──► x402 seller
│ 402 → sign → retry → content
└─ budget: daily cap
Networks: Base Sepolia (testnet) and Base mainnet. A fresh install defaults to Base Sepolia so you can try it with free test USDC. Base mainnet is supported and follows the same code path — switch to it in the plugin config and fund the wallet with real USDC (see Funding your wallet). Real funds are real money: you are responsible for what your agents spend.
Development
The release installer copies both the daemon binary and the QML plugin once
— the shell uses ~/.config/omarchy/plugins/gelu22.gateway/ and the daemon runs
~/.local/bin/gateway, never the repo. After editing Go or plugin/omarchy/,
deploy both to the live shell:
bash scripts/dev-sync.sh # build+install daemon, restart it, sync pluginReopen the panel (or omarchy restart shell) to reload. UI code only — budgets,
spend, sellers and remembered URLs are untouched.
Onboarding (no browser)
- Click the widget in the bar → enter email → enter the OTP code from mail
- Set daily spending limit (required): daily cap in USD
- Fund the wallet — the address is shown in the panel; click it to copy. A fresh install defaults to testnet, so you can fund it for free; see Funding your wallet just below.
Funding your wallet
The panel shows a wallet address that starts with 0x…. It is one address
with two separate balances: the same 0x… holds test USDC on the test network
and real USDC on Base mainnet, and the two never mix. Testnet funds are play
money; mainnet funds are real.
Test network — Base Sepolia (the default)
Free, no card required. Use it to try the gateway end to end.
- Copy the
0x…address from the panel (click it). - Open the Circle test faucet: https://faucet.circle.com/
- Choose Base Sepolia, paste your address, click Request tokens.
- The balance appears in the widget within a few seconds.
Main network — Base mainnet (real money)
Switch the network first, then fund it — otherwise the money lands on the wrong network.
- In the panel open Open config and change
paymentNetworkfromeip155:84532toeip155:8453. The daemon restarts on mainnet. - Add real USDC to the same
0x…address, on the Base network:- Buy with a card, Apple Pay or Google Pay through an on-ramp such as Coinbase Onramp, MoonPay or Kraken — pick USDC on Base.
- Or withdraw from an exchange you already use (Coinbase, Binance, Kraken, …):
buy USDC, choose Withdraw, paste the
0x…address, and select the Base network.
Pick the Base network, not Ethereum. USDC sent on the wrong network, or to a wrong address, cannot be recovered. Check the network and the address twice before you send.
Spend limits (two layers)
1. Daily budget — yours. One cap (default $5, set in the panel; 0 = always
ask). It is the only control on the sum you spend in a day:
- This layer is local. The cap and today's spent counter live in the daemon's own state files on this machine. A process running as your user can change them, so this budget guards against a mistaken or overeager agent — not against malware. The hard part is layer 2.
- Within budget: everything auto-pays — that is the point of the product.
- Above budget: the panel asks. The panel opens automatically when a payment needs approval; you get the amount, the seller and the URL ("Remember this URL", Cancel, Pay). Spend may exceed the cap until midnight, and every further over-budget payment asks again.
- Remember this URL pre-authorizes one endpoint (even above budget). The remembered list lives in the plugin config file, not inline in the panel.
2. Hard per-payment ceiling — enforced by Coinbase (X, 50 USDC in this deployment).
When the operator has configured it, a rule in Coinbase's Policy Engine, enforced
inside Coinbase's TEE before any signature, caps what a single payment may
move. It exists precisely because the daily budget is a file on your machine: the
ceiling is not, so tampering with that file cannot lift it.
- Pick
Xclose to your daily budget.Xis the most one single payment can move; the default budget is $5/day, so anXof about $5–10 is the natural fit. This deployment shipsX = $50, deliberately loose — lower it if you do not expect expensive purchases. - A single payment can never exceed the ceiling, whatever your daily budget says.
- Keep the daily budget ≤ the ceiling. A higher budget still works, but the ceiling clamps every single payment, so the excess can only leave in several payments.
- A payment above the ceiling is refused by Coinbase (
policy_violation) and cannot be approved from the panel — the panel shows the reason. - The ceiling is a per-signature limit, not a daily one: Coinbase's policy
engine has no daily sum. The number of payments per day is still bounded by
layer 1 — which is local and tamperable, so against stolen credentials
Xbounds each signature and your wallet balance bounds the total. - The ceiling is project-wide: it is one rule for the whole CDP project this
deployment uses, so it applies to every user of it (and project quotas are
shared). It is a safety ceiling, not a per-user budget. What that means for
availability (and how to isolate with your own
CDP_PROJECT_ID) is threat T11 in the project's threat model. - The ceiling is a deployment setting: the operator sets it in the CDP Portal (or with an API key), and this build does not carry the value. Nothing on your computer can change it — see the release notes for what your operator deployed.
Supported payment rail
What the gateway will sign for — and what it will not. This is a product contract, not a preference.
What it accepts (all must hold):
- scheme
exact - network on the allowlist (
eip155:84532Base Sepolia,eip155:8453Base mainnet) - asset = the pinned ERC-20 that supports EIP-3009 (USDC), settleable via CDP, and covered by the per-payment ceiling
payToa valid hex addressamount > 0(canonical decimal)
The loop: 402 (accepts[]) → checks (scheme/network/asset → policy → seller → budget) → sign (CDP TEE, EIP-3009) → retry with Payment-Signature → content.
Bounds per payment: daily budget (local), per-domain sub-cap (local), per-payment ceiling (Coinbase — not liftable from your machine), wallet balance.
Out of scope (by design): any non-exact scheme and any non-EVM rail
(e.g. Nano/XNO). The seller advertises the rail; a client cannot choose it. A
different rail family needs its own scheme, facilitator and trust model — a
companion project, not a config change.
MFA TOTP (optional, recommended)
Enable TOTP in the panel (QR code generated by the daemon, secret key backed up
by the user). CDP exposes no API to reset MFA — if you lose the secret key,
reset through the CDP portal (link in the panel). mfa_required blocks payments
(fail-closed). No recovery codes.
Plugin config
The plugin reads ~/.config/omarchy/x402-gateway/config.json (JSONC, comments
allowed). Manage it with "Open config" in the panel (opens your editor via
omarchy launch config editor):
Changing paymentNetwork restarts the daemon on the new network (a missing or
corrupt config keeps the running network — fail-safe). Budgets are NOT here:
they stay in the daemon's ~/.local/state/x402-gateway/policy.json.
Agent setup
MCP (Claude Code / opencode / Cursor / Codex / Gemini) — auto-detect and one-click Integrate per agent in the panel ("AI agents" section), or manually. Omarchy launchers without a known config file show up as installed and do not get an Integrate button.
{"mcp": {"x402-gateway": {
"type": "local",
"command": ["/home/you/.local/bin/gateway", "-mcp"],
"environment": {"GATEWAY_SOCKET_PATH": "/home/you/.local/state/x402-gateway/gw.sock"},
"enabled": true
}}}Tools: fetch_with_payment (pays 402 automatically), gateway_status,
gateway_pause.
HTTP gateway (any client):
curl --unix-socket ~/.local/state/x402-gateway/gw.sock -X POST \ http://localhost/fetch -H 'Content-Type: application/json' \ -d '{"url":"https://seller.example/paid-article"}'
Over-budget payments return budget_exceeded with can_override: true;
approved retries go through /fetch-override.
Money audit log
Every payment attempt appends one JSON line to
~/.local/state/x402-gateway/audit.log (0600): amount_micro, domain
(never full URL), outcome (paid / failed:<code>), override, timestamp.
Query it with
grep '"outcome":"paid"' ~/.local/state/x402-gateway/audit.log.
Daemon persists there too: policy-cap POSTs (policy caps old→new) and pause
transitions (paused); no-op writes stay silent. Panel UI decisions (approval
clicks, Save clicks) log to the journal instead
(journalctl --user | grep gelu22.gateway).
Monitoring
One command shows daemon state, spend vs caps, recent audit lines, and socket/config health (no writes to state files, no restarts; creates the empty state dir if missing):
gateway --status
Meaning of the output: daemon: is the live state (active, paused,
offline when the socket is unreachable); spend: is today's spend vs the
daily cap; audit: lists the last 10 audit.log lines
(no entries yet on a fresh install); config: shows the payment network
and remembered-URL count. A non-zero exit means something needs attention
(dead daemon, unreadable state/config file) — the message names the file.
Lower level, same data piecemeal:
journalctl --user | grep gelu22.gateway # panel UI actions + daemon lifecycle tail ~/.local/state/x402-gateway/audit.log # every payment attempt (JSONL) curl --unix-socket ~/.local/state/x402-gateway/gw.sock http://localhost/status
Security
- Private keys never leave the device (CDP MPC + device-generated secrets)
- Daily budget (mandatory at startup) plus a hard per-payment ceiling enforced at Coinbase, which cannot be lifted from this machine; single-instance lock — a duplicate daemon exits instead of risking session revocation
- Unix socket 0600/0700 — your user only
- Fail-closed: no network = no payment; one-click pause
- Enable NTP: EIP-3009 authorizations are valid in a ±5 minute window, so a wrong clock makes payments fail (the panel warns when the skew exceeds 60 s)
- Security policy and known limits: SECURITY.md
Backend (not part of the product)
An earlier Cloudflare Worker (device registration, budget sync and telemetry) is not built, not deployed and not published — it is parked as future work. The shipped product talks to Coinbase CDP only.
Architecture
| Component | Technology |
|---|---|
| Daemon | Go 1.25+, static binary |
| Plugin | Quickshell QML (Omarchy) |
| Backend | Cloudflare Workers + D1 (optional) |
| Payments | x402 v2, CDP signing, ERC-8021 builder-code attribution (s) |
License
MIT — see LICENSE.
来源:GitHub · github.com
