This content originally appeared on HackerNoon and was authored by Oluwajuwon Omotayo
\
What Eve is, and why it matters
Eve is Vercel's framework for building durable backend agents. The design is filesystem-first: you author an agent as a directory of files. instructions.md defines the always-on prompt. tools/ contains typed TypeScript tool definitions. skills/ defines reusable procedures. channels/ handles message ingress. subagents/ defines specialist child agents.
\
my-agent/
├── agent.ts
├── instructions.md
├── tools/
│ ├── transfer_funds.ts
│ └── get_balance.ts
├── channels/
└── subagents/
\
The framework compiles to inspectable artefacts under .eve/, exposes a stable HTTP protocol with sessionId and continuationToken, and manages the full lifecycle of agent runs, including durability across restarts. It runs on Vercel infrastructure but the design is clean enough that the concepts transfer.
At 2.2k stars and 153 forks with daily commits, Eve has genuine traction. The developer community is using it. Teams are building production agents on it.
And none of those agents has a governance layer — because Eve does not ship one.
The gap
Consider an agent with a transfer_funds tool.
Who decides whether a transfer above ₦5 million requires human approval? How do you prevent a subagent from autonomously calling that tool? If a BVN number appears in the tool input, how do you ensure it never reaches an audit log? If the agent is called from an unauthenticated channel, which tools are blocked entirely?
These are governance questions. You can answer them in several ways, all of which have problems.
Scattered across tools: Each tool implements its own approval logic and validation. This creates drift — when your compliance requirements change, you update some tools but miss others. There is no central view of what the governance model actually is.
Inside the agent loop: You modify how the agent processes tool calls. This couples your business rules to the framework internals. When Eve updates, your governance logic breaks.
Deferred entirely: Most teams do this. "We'll add compliance controls later." Later becomes never, or becomes a crisis when an agent does something it should not have.
The right answer is a dedicated governance layer: a composable, testable, auditable policy that sits between Eve's lifecycle events and your tools' implementations — evaluated consistently, independent of any individual tool's implementation.
That is eve-policy.
The architecture: two-phase evaluation
The most interesting design decision in eve-policy came from understanding Eve's lifecycle precisely.
Eve exposes two hooks on every tool:
needsApproval(ctx) — called synchronously before the turn completes. Its job is to return a boolean: should this tool call pause for human approval? The context available at this point is minimal: tool name and tool input. There is no session yet. This hook cannot be async.
execute(input, ctx) — called asynchronously when the tool actually runs. By now, the full session context is available: session.id, session.auth.current, session.parent (whether the caller is a subagent), The complete turn history.
This split creates a problem for governance. Some controls need input context only (is this amount above a reporting threshold?). Others need session context (is the caller a subagent?). A governance layer that evaluates only in execute misses the approval decision. A governance layer that evaluates only in needsApproval cannot see the session.
eve-policy evaluates the same policy twice — once in each hook — with a different context available each time.
needsApproval runs first (synchronous, input-only):
→ amountExceeds, fieldMatches, toolNameIs, fieldPresent ...
→ escalate? return true (agent pauses for human approval)
→ otherwise: return false
[human approves if needed]
execute runs second (async, full session):
→ isSubagentCall, principalTypeIs, alreadyApproved ...
→ deny? throw PolicyDenialError (tool never runs)
→ escalate? run tool + audit
→ audit? run tool + audit
→ allow? run tool
Why synchronous evaluation for the matchers? Governance decisions on the hot path cannot block on I/O. External lookups (database, API calls) belong in pre-execution middleware, not inside policy rules. All rule predicates in eve-policy are synchronous by design. Async work — audit logging — runs fire-and-forget after the decision is made.
What it looks like in practice
Wrapping an Eve tool with governance takes one line:
import { definePolicy, withNamedPolicy } from "eve-policy";
import { deny, escalate, audit } from "eve-policy/rules";
import { amountExceeds, isSubagentCall, toolNameIs, fieldMatches, and } from "eve-policy/rules";
import { ConsoleAuditLogger } from "eve-policy/audit";
const transferPolicy = definePolicy({
name: "transfer-controls",
version: "1.0.0",
rules: [
// Hard deny: accounts cannot transfer to themselves
deny("no-self-transfer",
ctx => ctx.toolInput.source === ctx.toolInput.destination,
"Source and destination cannot be the same"),
// Escalate: large transfers require human approval
escalate("ctr-threshold",
amountExceeds("amount", 10_000),
"Exceeds $10,000 CTR threshold — compliance review required",
{ riskLevel: "critical", owaspRisks: ["ASI02"] }),
// Escalate: subagents cannot autonomously execute financial writes
escalate("subagent-financial-write",
and(isSubagentCall(), toolNameIs("transfer_funds")),
"Subagent financial write — principal separation required",
{ riskLevel: "critical", owaspRisks: ["ASI03"] }),
// Deny: card PANs must never appear in tool input
deny("no-card-pan",
fieldMatches("*", /\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\b/),
"Card PAN in tool input — ASI02 data exfiltration vector",
{ riskLevel: "critical", owaspRisks: ["ASI02"] }),
// Audit: log all transfers regardless of other decisions
audit("all-transfers",
toolNameIs("transfer_funds"),
"All fund transfers logged for compliance"),
],
});
// One line — drop-in replacement for the original tool
const safeTool = withNamedPolicy(
"transfer_funds",
transferTool,
transferPolicy,
{ auditLogger: new ConsoleAuditLogger() }
);
withNamedPolicy returns a new ToolDefinition — same shape, same interface. Eve cannot tell the difference. The governance layer is transparent to the framework.
The built-in profiles
Shipping your own rules from scratch is possible — definePolicy accepts any predicate. But for common cases, eve-policy ships two production-ready profiles.
owaspTop10Policy covers all ten risks in the OWASP Top 10 for Agentic Applications (2026): goal hijacking, tool misuse, identity abuse, supply chain risks, code execution, context poisoning, inter-agent communication risks, cascading failures, trust exploitation, and rogue agent behaviours. This is 20 rules that address the known attack surface for AI agents in production.
financialBaselinePolicy covers jurisdiction-agnostic controls for financial agents: deny self-transfers, negative amounts, sanctioned countries (OFAC/FATF), card PANs and CVV in input; escalate at USD/NGN/KES CTR thresholds, international wire transfers, KYC bypass patterns, and subagent financial writes; audit customer PII access and moderate-value transactions; allow read-only tools explicitly.
Compose them with deny-wins semantics:
import { compose } from "eve-policy";
import { owaspTop10Policy, financialBaselinePolicy } from "eve-policy/profiles";
const myAgentPolicy = compose(
owaspTop10Policy,
financialBaselinePolicy,
myDomainPolicy,
);
// If any component is defaultEffect:"deny", the composed policy is deny.
// Rules evaluate in order. First match wins.
Why fail-closed matters
The default defaultEffect in eve-policy is "deny".
When no rule matches a tool call, the decision is to block it — not to permit it. This is a deliberate constraint that reflects a basic security principle: an agent governance system that silently permits unmatched calls is not a governance system. It is a logging system.
If your intended posture is allow-by-default with specific restrictions, set defaultEffect: "allow" explicitly. The explicitness is the point. You are forced to make a conscious decision rather than discovering your governance model is permissive by accident.
Testing without a runtime
evaluatePolicy runs policy evaluation synchronously without needing a live Eve agent. Combined with InMemoryAuditLogger, you can write policy tests with standard unit testing tools and no external dependencies.
import { evaluatePolicy } from "eve-policy";
it("escalates transfers above $10,000", () => {
const decision = evaluatePolicy(
transferPolicy,
{ toolName: "transfer_funds", toolInput: { amount: 15_000 }, approvedTools: new Set() }
);
expect(decision.effect).toBe("escalate");
expect(decision.owaspRisks).toContain("ASI02");
});
No agent process. No network calls. Fast, deterministic, runnable in CI.
What comes next
The jurisdiction-specific African regulatory policy packs — NDPA 2023, CBN AML/CFT, NFIU structuring detection, POPIA, Kenya DPA — are being built as part of a separate package called @comply54/adapter-eve. It will provide pre-composed profiles for direct use with eve-policy and is currently under active development.
The underlying policy corpus in OPA format is already open source as agt-policies-nigeria, which was contributed to Microsoft's Agent Governance Toolkit earlier this month. The @comply54/adapter-eve package will port those frameworks into eve-policy's TypeScript rule format.
A pattern worth naming
The thing I keep noticing as I build these governance layers is that major frameworks consistently launch without them.
Eve launched without governance. Microsoft's AGT launched without African regulatory coverage. Most agent frameworks treat compliance as someone else's problem — the application developer's, the compliance team's, the regulator's.
But the compliance layer is not separate from the technical layer. It has to be designed for the framework's specific lifecycle. It has to integrate at the right abstraction level. It has to be testable, composable, and auditable in ways that make sense to the engineering team, not just the compliance team.
That design work is engineering work. And until someone does it explicitly, teams either skip it or scatter it across their codebase in ways that create exactly the kind of drift that causes incidents.
eve-policy is my attempt to do that design work for Eve explicitly — and to make it available to every team building on Eve, rather than every team reinventing it from scratch.
eve-policy is available at github.com/kingztech2019/eve-policy and npmjs.com/package/eve-policy. MIT licensed. Issues, PRs, and use cases welcome.
\
This content originally appeared on HackerNoon and was authored by Oluwajuwon Omotayo
Oluwajuwon Omotayo | Sciencx (2026-06-29T00:34:12+00:00) Extending Vercel’s Eve With a Policy-Driven Governance Layer. Retrieved from https://www.scien.cx/2026/06/29/extending-vercels-eve-with-a-policy-driven-governance-layer/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.