
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
@moneolabs/guard
Advanced tools
Spending policy for AI agents, evaluated before anything is signed.
npm install @moneolabs/guard
The guard sits between what an agent intends and what actually gets signed. It answers with a verdict, a reason, and the version of the policy that decided, then writes all three to a ledger. A blocked payment is never broadcast, so it costs nothing.
This package has no service behind it and needs none. It is pure evaluation over a ledger you can keep wherever you already keep financial records.
import { createGuard } from "@moneolabs/guard";
const guard = createGuard({
perTransaction: { max: "$250" },
rolling24h: { max: "$2,000" },
velocity: { max: 20, per: "1m" },
counterparties: "allowlist-only",
allow: ["x402:*", "0x9f3c...a71b", "stripe:acct_1Nz"],
deny: ["mixer:*"],
escalate: { above: "$500" },
});
const decision = await guard.check({
action: "transfer",
to: "0x9f3c...a71b",
amount: "$8,200",
agent: "ops-03",
});
decision.verdict; // "block"
decision.reason; // "$8,200.00 exceeds the rolling 24h budget: $2,000.00 cap, $1,880.00 used, $120.00 left"
decision.rule; // "budget.24h"
check() does not just answer, it reserves. An allowed decision counts against the budget from the
moment it is made, because a payment in flight is money you no longer have. Tell it what happened:
const decision = await guard.check({ action: "pay", to: "vendor:acme", amount: "$100" });
if (decision.verdict === "allow") {
try {
const receipt = await payVendor();
await decision.settle(receipt.amount); // pass the real amount if it differs
} catch {
await decision.release(); // it never happened, give the budget back
}
}
Forgetting to settle is safe. Forgetting to release is not, which is why wrap() exists.
const payVendor = guard.wrap(
async (vendorId: string, usd: number) => internalPayments.send(vendorId, usd),
{
action: "pay",
amount: (_vendor, usd) => `$${usd}`,
to: (vendor) => vendor,
},
);
await payVendor("acme", 40); // settles on success, releases on throw
await payVendor("acme", 9000); // throws PolicyDeniedError, never calls through
Above a threshold, the decision holds instead of blocking and waits for a person.
const decision = await guard.check({ action: "pay", to: "vendor:acme", amount: "$900" });
if (decision.verdict === "hold") {
const outcome = await decision.wait({ timeout: "4h" });
if (outcome.granted) await wallet.pay(/* ... */);
}
// Answered from wherever your approvals live:
guard.resolve(decision.approvalId!, { granted: true, by: "finance@example.com" });
Approvers are pluggable: manualApprover, autoApprover, loggingApprover, webhookApprover, or
your own. A held movement does not consume budget until it settles.
Evaluated in this order. The first block wins, so a banned counterparty is never reported as merely over budget.
| Rule | Blocks when |
|---|---|
deny | The counterparty matches a denylist pattern. |
assets | The asset is denied, or is outside an allowlist. |
actions | The action kind is denied, or is outside an allowlist. |
counterparties: "allowlist-only" | The counterparty is not on the allowlist, or is missing entirely. |
perTransaction | The USD value of one movement is over the cap. |
velocity | Too many movements inside the window. |
budgets / rolling24h | The rolling window would go over its cap. |
escalate | Nothing above blocked, but it needs a human. Produces hold. |
Patterns use * as a wildcard and match case insensitively, so x402:* covers every x402 endpoint
and 0xABCD matches 0xabcd.
An intent with no counterparty is blocked under allowlist-only. Omitting a field must never be a
way around a rule.
Limits are in USD, agents move whatever they hold. Pass a PriceSource and the guard values each
movement before applying a limit.
import { fixedPrices } from "@moneolabs/core";
const guard = createGuard(
{ perTransaction: { max: "$100" } },
{ prices: fixedPrices({ AAPLX: 228.41 }) },
);
await guard.check({ action: "trade", amount: "2 AAPLX" });
// blocked: "$456.82 exceeds the $100.00 per-transaction limit"
Dollar-pegged assets skip the lookup. Anything else without a price is refused rather than guessed.
import { simulate, eventsFromLedger } from "@moneolabs/guard";
const report = await simulate(proposedPolicy, eventsFromLedger(lastMonth));
report.counts; // { allow: 412, hold: 6, block: 19 }
report.blockedUsd; // what the new policy would have stopped
report.results[0]; // per movement: verdict, rule, reason
Nothing real is touched. This is how you find out that a budget would have blocked a third of last month before it blocks a third of next month.
await guard.usage(); // what is left of each budget
await guard.history({ verdict: "block" }); // every refusal, with its reason
Blocked attempts are kept, not discarded. They are the most useful thing you own when tuning a policy: they are the record of what your agents actually tried to do.
Bring your own store by implementing DecisionLedger. The default is in memory.
Pass a manualClock and rolling windows become instant.
import { manualClock } from "@moneolabs/core";
const clock = manualClock(0);
const guard = createGuard({ rolling24h: { max: "$100" } }, { clock });
await (await guard.check({ action: "pay", amount: "$100" })).settle();
await clock.advance("25h"); // the window rolls, the budget is free again
MIT
FAQs
Spending policy for AI agents, evaluated before anything is signed.
The npm package @moneolabs/guard receives a total of 165 weekly downloads. As such, @moneolabs/guard popularity was classified as not popular.
We found that @moneolabs/guard demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.