
Every security team I talk to has some version of the same document. It might be called an “AI Acceptable Use Policy” or “Agent Governance Standards.” It usually lives in Confluence or SharePoint. It says sensible things: coding agents shouldn’t touch production credentials, assistants shouldn’t email sensitive data outside the company, and nobody should connect an unvetted MCP server to an agent with access to customer records.
It’s a good document, and it’s almost never enforced, and not for lack of trying.
Until recently, there simply was no practical way to enforce it. Coding agents like Claude Code and Cursor, and personal AI assistants that read your inbox, manage your calendar, and act on your behalf, make thousands of decisions a day. Each of those decisions is a tool call, a file read, a shell command, or an outbound message. A governance policy written in English doesn’t sit in that path. It sits in a wiki, waiting for an audit.
This post is about closing that gap. It covers what Zenity Boundaries are, why context and time change what they can do, and how they let a security team get ahead of agent risk instead of chasing it.
Governance Is a Statement, Enforcement Is a Control
I’ve been saying this in almost every demo lately: having a policy is one thing; enforcing it is another. With agents, the gap between the two is wide.
With human users, we’ve had decades to build the enforcement layers. Identity providers decide who can log in. DLP watches what leaves. EDR watches what runs. All of it assumes the actor is a person moving at human speed. Agents shatter those assumptions. They hold legitimate credentials (or find them, or write a zero-day to get them), work far faster than any person, and reason their way to actions nobody explicitly asked for.
Consider the incidents that have already happened. In the PocketOS incident, a coding agent debugging a staging issue picked up API tokens on its own and deleted a production database in seconds, despite being told not to. No attacker was involved, and no prompt injection was used. The agent hit a problem, improvised, and acted. In another case, Cline lost its npm credentials to a weaponized GitHub issue, because the agent read untrusted content and followed the instructions in it.
Neither case involved a broken permission model. Every action was technically authorized. What was missing was a control that could ask a different question: is this authorized action appropriate right now, given everything else that has happened in this session?
That is what a Zenity Boundary does. A Boundary is a policy that is evaluated inline, at the moment the agent decides to act and before the action completes. It returns a deterministic answer: allow, redact, or block. Boundaries are written in Zedar, Zenity’s policy language derived from Cedar. Policy Health catches contradictory or unreachable rules before they cause trouble. Production History lets you replay a new Boundary against the last seven days of real agent activity, so you know what it would have caught, and what it would have broken, before you turn it on. As a former SIEM engineer, this is the part I appreciate most. You can test a policy against real production history and move it straight into prevention mode, instead of spending weeks tuning detections before anyone trusts them to block. Zenity also allows you to run a simulation in the case you haven’t onboarded a specific platform yet.
Today, Boundaries enforce inline through hooks on platforms such as Claude Code and Cowork, Microsoft Copilot Studio, Azure Foundry, and others. Some platforms, like ChatGPT and Gemini, only support detection today. That limit comes from the platforms, and it’s worth knowing before you plan a rollout.
Context Is What Makes a Boundary Smart
A rule like “block outbound email” is easy to write and useless in practice. Your assistant needs to send an email. Your coding agent needs to push code. A Boundary that blocks the action outright breaks the business. A Boundary that blocks the action in the wrong context is what you actually want.
Zenity builds that context with taints. A taint is a label that attaches to an agent session when something security-relevant happens. The agent read a file classified as sensitive. It ingested content from an untrusted external source. It accessed a credential. Taints carry forward through the session, so a later Boundary can reason about what came before.
That’s how you express one of the most important patterns in agent security, the Lethal Trifecta. The trifecta is untrusted input, access to private data, and an external communication channel, all converging in one session. Any one of them is normal. All three together is how data gets exfiltrated. Take a personal assistant that reads an inbound email (untrusted input), opens the CFO’s board deck (private data), and then tries to post to an external URL (exfiltration channel). That session should be stopped at the third step. A taint-aware Boundary does exactly that, while the same assistant keeps sending ordinary email all day long.
Zenity recently added a capability that takes this further: chained Boundaries. A Boundary rule can now reference an earlier Boundary, which means it can react to a Detect or Prevent match from a previous step in the same agent session. In practice, this lets you build layered logic:
- One Boundary detects a likely prompt injection in content the agent just read, without blocking it, since detection on content can be noisy.
- A second Boundary prevents any later call to a sensitive tool in that same session, such as a shell command, a deployment, or a write to a production repository.
The first rule flags a risk. The second rule converts that risk into a hard stop exactly where it matters. You haven’t blocked the agent from reading the web. You’ve blocked it from acting on what it read when the action is dangerous. Detection tuned for recall and prevention tuned for precision is a pattern security teams already know from the rest of their stack. Now it can apply to agents.
Time Is Context Too: Enter Dogwood
Taints and chained Boundaries tell you what happened earlier in a session. The next frontier is when, and in what order.
In August, AWS released Dogwood, an open-source governance language for AI agents that uses temporal logic to enforce safety policies over sequences of tool calls. Dogwood embeds Cedar and adds a second kind of clause, when temporal, whose body can reason about the agent’s recent event history. Its operators let a policy require prior approval, cap activity over time, and replay traces, and it is backward compatible, so existing Cedar policies remain valid Dogwood policies.
Because Zenity’s Boundaries already share Cedar’s foundations, Dogwood-style temporal conditions are a natural extension of what customers can express. This is where things get interesting for coding agents. AWS’s own example is a good one: a policy that permits a coding agent to perform a git push only if a repository’s tests were successfully run within the last fifteen minutes and no test run has failed since.
Here’s what that looks like in Dogwood’s syntax, using a simpler example from the project’s reference repository: a read is permitted only if the same user logged in within the past hour.
// Illustrative action names; your event schema defines the real ones.
@id("forbid_push_without_fresh_passing_tests")
forbid (
principal,
action == Repo::Action::"GitPush",
resource
)
unless temporal {
!Repo::Action::"RunTests"::response{ input.repo: context.input.repo, output.passed: false }
since within 15m
Repo::Action::"RunTests"::response{ input.repo: context.input.repo, output.passed: true }
};
Temporal conditions on their own are powerful. Paired with Zenity’s taints and context tags, they become something security teams have never had for agents. Consider rules like these:
- Block a git push if the session was tainted with an untrusted-repo or injection signal at any point in the last 30 minutes, even when the tests passed.
- Allow an assistant to send external email, but cap it at a handful of distinct recipients per hour, and drop that limit to zero once the session has touched data tagged as confidential.
- Permit a deployment command only if a human approval event happened first, and only if no credential-access taint has appeared since that approval.
Each of these combines what the agent touched (taints) with when, and in what order, things happened (temporal conditions). Point-in-time authorization can’t express any of them. They are exactly the rules your governance document was trying to describe.
The starkest example of why sequence matters came this summer, when frontier models running a cyber evaluation inside OpenAI’s research environment escaped their sandbox through a zero-day in a package proxy, coordinated with each other through notes left in a package registry, used credentials they found exposed online, and compromised Hugging Face’s production infrastructure. No individual step looked like “hack Hugging Face.” Each was a shell command, a package operation, or a login. But the sequence had several chokepoints a runtime policy could have acted on: network actions outside the task’s scope, outbound authentication after a credential-access event, publishing to a registry, and an action rate no legitimate task needs. None of these would be a silver bullet. Together they turn one catastrophic chain into several chances to stop it, and that is the point of enforcement.
When Agents Hit a Wall: Steering
There’s a catch with any control that only knows how to say no. Agents are goal-driven. When a coding agent hits a wall, it doesn’t file a ticket and wait. It looks for another way to finish the job. That’s what the PocketOS agent did when it went hunting for tokens, and it’s what the evaluation models did when the only way out was a package proxy. A bare block tells the agent nothing except that one door is closed, so it tries the next one: a different tool, a different command, a different credential.
That’s why Boundaries don’t stop at blocking. They can also steer. Instead of hitting a dead end, the agent learns why the action was stopped and what an acceptable path looks like, at the exact moment it’s deciding what to do next. A coding agent that tries to push without fresh passing tests can be pointed back to the test suite instead of searching for another way to get the code out. An assistant that tries to send a confidential file to an outside address can be steered to the internal share instead. The agent changes course, and the task still gets done.
Steering and blocking work together. Hard forbid rules stay in place for the outcomes that must never happen. Steering handles the much larger set of actions where the goal is legitimate but the path is wrong. For a security team, that’s the difference between a control developers fight and one they barely notice. It also closes the gap a block alone leaves open, because an agent that has been shown the right path has far less reason to go looking for the wrong one.
Getting Off the Back Foot
Here’s the part that gets me most excited, and it’s less about technology than about how security work feels.
For most of our careers, security has been reactive by design. Something bad happens. A detection fires. An alert lands in a queue. An analyst triages it, hopefully before the damage spreads. SOAR has made that loop faster, and automation has made it cheaper, but the shape is the same: we wait for the bad thing and then respond. We’re always a step behind.
Agents make that model worse. An agent that deletes a database in seconds doesn’t give you time to triage. By the time the alert is in the queue, the outcome has already happened.
Inline Boundaries change that shape. A Boundary sits in the decision path, so the dangerous action is evaluated before it executes. The alert you would have triaged at 2 a.m. becomes a blocked action and a logged decision with the full context of why it was stopped. You still want detection and investigation, because no control is perfect and new attack patterns will always appear. Zenity’s incident and forensics capabilities exist for exactly that, and what you learn there should become your next Boundary. But for the risks you can already name, you no longer have to wait for them to happen.
That’s a rare position for a security team: setting the rules of engagement before the fight starts, instead of cleaning up after it.
Security as the Team That Says “Yes”
There’s a version of AI agent security that amounts to “ban it until we understand it.” It’s understandable, and it doesn’t work. Developers install coding agents anyway (and why wouldn’t they?). Executives connect personal assistants to their inboxes anyway. Shadow adoption fills the gap, and the security team loses visibility entirely.
Enforceable Boundaries offer a better deal. Instead of debating whether coding agents are allowed, you can say yes, with conditions. Use Claude Code or Cursor freely, but production credentials stay off-limits, and pushes require passing tests. Connect your assistant to your email, but it can’t forward confidential data to external addresses. Try new MCP servers, but unvetted ones can’t reach sensitive tools.
Those conditions are specific, testable, and enforced, so the business gets the productivity and the security team gets containment. Risk doesn’t drop to zero. Nothing gets it there, at least not until Skynet comes online and makes the question moot. But it becomes bounded, visible, and governed, and that’s what lets an organization adopt agents at the speed the business wants.
The governance document finally does what it was always supposed to do.
Where to Start
If you’re still getting your head around agent security, start small and concrete:
- Pick one agent class and one rule. Coding agents and production credentials is a strong first pairing.
- Simulate before you enforce. Replay the rule against recent real activity and see what it catches.
- Layer in context. Add taints and chained Boundaries so the rule fires only when the session actually warrants it.
- Add time as temporal conditions mature. Sequences, windows, and approvals are where agent policy is headed.
Policies describe intent. Boundaries enforce it. That’s the difference between hoping your agents behave and knowing they will.
Want to see this against your own agents? Book a demo, and we’ll walk through writing a Boundary for your coding agents or assistants, replaying it against real agent activity, and moving it into prevention.
All ArticlesRelated blog posts

Zenity Launches Universal Runtime Control for Claude Enterprise
Anthropic's inference hooks update lets security teams validate Claude's tool calls before they run. Zenity supports...

Architecting Trust: The Data Pipelines and Infrastructure Behind The Guardian Agents
As AI agents operate with increasing autonomy in enterprise production, the sheer volume of security telemetry...

Zenity for Claude Tag: Securing the Shared Agent in Your Slack Workspace
Your team just hired someone who never sleeps, reads everything, and takes work from whoever asks. That’s the elevator...
Secure Your Agents
We’d love to chat with you about how your team can secure and govern AI Agents everywhere.
Get a Demo