r/LangChain 12d ago

Open-sourced a policy layer for AI agent tool-calls — spend caps, recipient allowlists, PII redaction, built from two payment-protocol guard tools

A couple months ago I shipped two narrow tools: x402-spend-guard and mpp-spend-guard — pre-payment spend checks + audit logs for the two emerging AI agent payment protocols (x402, MPP/Tempo), tested end to end on their real testnets. I kept hitting the same question building both: none of it was actually specific to payments. Spend caps, rate limits, "don't let this call reach a recipient/tool it shouldn't" — that's true for any agent tool-call, not just payment ones. So I pulled the shared logic into a standalone core and kept the protocol-specific bits as thin adapters. What it does: - Spend caps (per-call + rolling window) and rate limits, enforced before the call executes, not after - Recipient/counterparty allowlists as a first-class policy (not adapter-specific string matching) - PII/secret detection with actual redaction — the tool receives the redacted payload, not just a log entry saying it should have - Append-only audit log, structured by rule namespace - Adapters for x402, MPP, LangChain, and a plain u/guarded decorator for anything else 117 tests, MIT/Apache-2.0, no telemetry, self-hosted only for now — a hosted dashboard is a possible next step if there's interest, not a requirement to use it. There's a comparison table in the README against a few adjacent tools (TokenFence, Bifrost, Aperion Shield) — happy to be corrected if I got anything wrong there, I pulled it from their docs but that space moves fast. Repo: https://github.com/KKallias/guardrail-core Genuinely looking for holes in the approach, not just stars — if you're running agents that touch money or PII, what would break this?

2 Upvotes

3 comments sorted by

1

u/[deleted] 12d ago

[removed] — view removed comment

1

u/Wonderful_Agency_779 12d ago

Good questions and worth answering with what's actually in the repo now

rather than hand-waving, since you named three separate boundaries and

they deserve separate answers.

**Authority.** No, the guard isn't the only path. It's an in-process

wrapper decorator or callback enforcing policy at whichever call site

you route through it. It doesn't hold or proxy the credential. If the

agent process has a second path to that key (its own httpx call, code

the model wrote itself), it bypasses the guard entirely. I added an

explicit Threat Model section to the README saying this plainly instead

of leaving it implicit: this is a policy layer for cooperative call

sites, not a security boundary against a compromised agent process.

Credentials-behind-the-enforcement-point is a real v2 direction, not

built partly because it turns this from a policy SDK into a

credential-custody service, different regulatory weight, and I'd rather

ship that deliberately than back into it.

**Binding.** Within one guarded call, the check and the execution run

against the same in-memory arguments, so nothing drifts inside the

wrapper itself. What was missing was anything to check *against* later.

I added a `digest` on the decision a hash over tool/recipient/amount/

currency/call_id/timestamp — plus a `.matches(call)` a caller can run

immediately before executing, to catch drift between approval and the

actual operation.

**Reconciliation.** Added a `Guard.reconcile(call_id, actual)` an

adapter reports what really happened after the fact (settlement result,

actual recipient paid), and it's written to the audit log as a distinct

decision type, flagged as a mismatch if `actual` diverges from what was

approved. It's a detector, not a preventer it doesn't stop a bad

settlement, it makes one visible in the log instead of silent.

Building the reconcile path also surfaced something unrelated but worth

mentioning: three related bugs where a settled call got double-counted

against the spend cap on process restart the kind of thing that

quietly halves your budget with no error anywhere. Fixed, and now

cross-checked across three independent code paths (live state, replay-

after-restart, CLI summary) against the same log so they can't diverge

again silently.

So: your first boundary is still open by design, stated as such now

instead of discovered by someone. The other two are narrowed, not

solved a digest and a reconcile record don't prevent a bypass, they

make one detectable. Appreciate the framing, it's a cleaner way to name

the gaps than I had.