Your gateway asks one question before an agent acts: POST /v1/decide. Wardryx answers allow, deny or hold in one deterministic pass. No LLM sits in the decision path, so the same policy and the same request always return the same verdict, and a big spend waits for a human who signs off with a short-lived token. The pattern is old and good: PDP/PEP, XACML's good ideas, pointed at what agents do.
This is a simulation, and it says so. It replays the shape of the live validation record: a 34-request concurrent burst, different agents, budgets and policies, all hitting /v1/decide at once. In the real run the filtering came back exact: 6/6 permission oversteppers denied with 403, unattested calls denied until attested, 176 decisions across the full campaign.
The PEP posts the proposed action: agent, tools, declared domains, step count, estimated cost, attestation. Wardryx matches policies by agent:// glob and applies the rules in a fixed order, where deny_above_usd outranks every approval and the hold check comes after it. The verdict returns with a cacheable flag, the caller's cache keys on agent, tool set and attestation, and every decision leaves as an event.
Every answer is allow, deny or hold. No LLM sits in the decision path, so the same policy set and the same request return the same verdict, every time. A decision you can replay in a test is a decision you can defend in an audit.
require_human_above_usd turns a big spend into a hold, not a guess. A human grants it out of band, and Wardryx mints a signed approval token bound to the cost the human actually saw. The agent resubmits with the token; nothing parks a connection waiting for a signature.
deny_above_usd is the rule no token can talk past. It is checked before the hold logic ever runs, so even a granted approval cannot authorize a spend over the ceiling. Some numbers should end the conversation, and this one does.
deny_if_unattested demands a live attestation before an agent touches the sensitive rules. Methods are trimmed and lowercased on the way in, so a literal “None” string reads as no attestation instead of sneaking past as a value.
When the PDP is unreachable, the enforcement point picks the failure mode per deployment. Fail-open keeps traffic moving and silently drops policy for the outage; fail-closed blocks every governed action until Wardryx is back. The default is documented, and so is the tradeoff.
PUT, GET and DELETE /v1/policies/{id} manage a runtime policy layer with validate-then-apply semantics; the file-loaded set stays a permanent floor no API write can erase. The taipan_wardryx_policy resource drives the same API from Terraform.
Two substitutes get proposed for a policy plane: asking the model to behave in the system prompt, and pointing at the IAM you already run. Keep your IAM; it answers a different question. Neither substitute sees tools, dollars and runs in one place.
| Wardryx | “Prompt the model to behave” | IAM alone | |
|---|---|---|---|
| Deterministic | Yes: same input, same verdict | No | Yes |
| Knows about cost | Holds above $X | No | No |
| Human approval flow | Built in, token-bound | No | Ticket queue |
| Sees declared-but-not-invoked tools | Yes, since the live-found bypass was fixed | n/a | No |
| Auditability | Every decision an event | Vibes | Partial |
TokenFuse's PEP hook asks Wardryx on every request and stamps the verdict on the response. Each decision lands on the bus as a source: wardryx event, which Idryx correlates into the identity graph. Mockryx rehearses the denials in pre-prod, so the first real no is never the first no ever. And the policies themselves are code: the Platform page's Terraform provider creates, changes and destroys them like any other resource.
Boots on :8090 with an in-memory store: allow-all until you load a policy, and it logs that choice. Then dry-run a policy offline against a directory of Agent Passports: