for agents · spend policy
A budget, not a blank check.
Guard sits between your agent and every 402. It decides whether to pay, signs locally if the answer is yes, and logs every cent against the task that spent it.
npm i @coinslot/guardpackages land on npm with v0.1 — until then install from the repo
{ stop at the budget }
- kill_switchnot pressed
- budget$0.12 of $0.50
- per_call$0.05 > $0.01
- dailynot checked—
- payto_pinnednot checked—
The 402 asks for more than the per-call max
Agent quickstart
Give it a wallet. And a budget.
Guard wraps your x402 client and fetch. Every 402 goes through your policy before anything is signed: budget per task, cap per call and per day, sellers you allow, addresses you pinned.
- Task budgets, per-call caps, a daily cap
- Allow-lists and payTo pinning
- Keys never leave your machine
import { createGuard, fileLedger } from "@coinslot/guard"
import { privateKeyToAccount } from "viem/accounts"
import type { Hex } from "viem"
// The signer stays on this machine. Guard never sends keys anywhere.
const signer = privateKeyToAccount(process.env.AGENT_KEY as Hex)
const guard = createGuard({
signer,
perCall: "$0.01",
daily: "$5",
allow: { domains: ["api.example.com", "*.weather.dev"] },
ledger: fileLedger(".coinslot/ledger.json"),
})
guard.on("blocked", (e) => console.warn("blocked:", e.rule, e.reason))
guard.on("paid", (e) => console.log("paid", e.amount, "left", e.budgetLeft))
const research = guard.task("research", { budget: "$0.50" })
const res = await research.fetch("https://api.example.com/v1/fact")
console.log(await res.json())policies
Ten controls between your agent and its wallet.
Task budget
A spending limit for one job. Every payment inside the task counts against it, and the call that would cross it is refused.
const research = guard.task("research", { budget: "$0.50" })
await research.fetch("https://api.example.com/v1/fact")
// the call that would cross $0.50 → blocked · budget{ stop at the budget }
policy builder
Write the policy. Copy the config.
Answer what you'd tell a new hire with a company card. The config on the right goes straight into createGuard.
import { createGuard, fileLedger } from "@coinslot/guard"
const guard = createGuard({
signer,
network: "base",
perCall: "$0.01",
daily: "$5",
allow: { domains: ["api.example.com", "*.weather.dev"] },
deny: { domains: ["ads.example.org"] },
pinPayTo: true,
onNewSeller: async (seller) => askOnTelegram(seller),
ledger: fileLedger(".coinslot/ledger.json"),
})
const research = guard.task("research", { budget: "$0.50" })NON-CUSTODIAL
Your keys stay home.
- 01
Guard runs inside your agent and signs locally. Private keys are never sent to Coinslot.
- 02
Only events leave the machine, and only if you turn on the dashboard: amounts, addresses, rules, trace ids.
- 03
payTo pinning is on by default, so a seller's address can't be swapped after you approve it.
- 04
We never hold funds. Payments go agent → facilitator → seller.
- 05
The kill switch is a stop signal, not a key. It can block payments, never make one.
- 06
MIT-licensed. Read every line that touches your wallet.
faq
Fair questions.
{ stop at the budget }
No. Guard runs entirely in your agent's process. The dashboard, alerts and kill switch are optional extras.
Any x402 client signer: a viem account on Base, a keypair on Solana. The signer stays where it is; Guard only decides when to use it.
No. Guard works with any API that speaks x402 V2.
Guard doesn't sign. It emits a blocked event with the rule and a plain-language reason, and your agent decides what to do next.
In memory by default. Pass fileLedger(".coinslot/ledger.json") on persistent disk so a retry after a restart is covered too.
Let it pay. Just not forever.
{ stop at the budget }