AI agents can call APIs, use MCP tools, access databases, modify files, and trigger real-world actions.
So the important security question isn't just:
Is the agent trustworthy?
It is:
Is this specific action authorized right now?
That is the problem I built Agent Firewall to solve.
How it works
The architecture is intentionally centered around one authorization boundary:
AI Agent
|
v
Tool / API Request
|
v
FirewallSDK.authorize()
|
+--> identity
+--> task
+--> capability
+--> provenance
+--> delegation
+--> constraints
+--> validity
+--> revocation
+--> policy
+--> security state
|
v
ALLOW / DENY
|
v
Execution
The important part is not the number of checks.
It is where authority comes from.
Security analysis can provide evidence and context.
It cannot create an authorization decision.
A risk score cannot become an allow.
An LLM cannot become an allow.
Monitoring cannot become an allow.
Aegis cannot become an allow.
The canonical authorization boundary remains responsible for the decision.
Capabilities
Agent Firewall treats permissions as signed capabilities.
A capability can carry:
identity
action
constraints
issuer
expiration
delegation
nonce
signature
For example:
payments.send
amount_max = 1000
expires_at = ...
Authority can be delegated or attenuated, but not widened.
A child capability can become:
amount_max = 100
It cannot turn itself into:
amount_max = 10000
The same idea applies across delegation lineage.
Every step must preserve or reduce authority.
Aegis
v2.4 introduced Aegis, the adaptive authority control plane.
It allows live authority to be:
narrowed
suspended
revalidated
revoked
while a task is running.
But Aegis has a very deliberate limitation.
It can restrict authority.
It cannot grant authority.
Its integration with the SDK is essentially:
Aegis
|
v
deny-only gate
|
v
FirewallSDK.authorize()
There is no Aegis → ALLOW path.
That separation is important because otherwise the system would eventually have two authorization engines.
Then came v2.5
I decided to stop adding features and attack what already existed.
The v2.5 mission was:
Attack the shipped boundary until a guarantee breaks, the attack is contained, or a limitation becomes clear.
I ran 22 attacks against v2.4.
And we found real problems.
An expired capability could be allowed
The most serious finding was an expiry check that could silently abstain when the firewall could not establish the current time.
That meant a legitimate signature could still result in:
expired capability
|
v
authorized
v2.5 changes that to:
clock unavailable
|
v
DENY
Internal security state could raise exceptions
Failures in:
- revocation
- delegation lineage
- issuer trust
- risk state
- refusal state
could previously escape authorize().
That matters because an exception isn't a security decision.
v2.5 turns those failures into explicit denials such as:
revocation_state_unavailable:{Type}
The boundary decides.
The caller does not.
Authorization and execution could disagree
Three integrations had another interesting problem.
The firewall authorized one representation of a request, while the handler could receive another.
For example:
authorize({"amount": 10})
|
v
ALLOW
handler({"amount": 5000})
The authorization boundary itself was correct.
The integration around it wasn't.
v2.5 fixes this structurally by normalizing or settling the request once and giving the same object to both authorization and execution.
Authority envelopes
v2.4 also introduced AuthorityEnvelope.
It provides a projection of what a capability can still do after considering its constraints and delegation lineage.
But an envelope is deliberately not an authorization decision.
The guarantee is one-way:
Envelope excludes request
|
v
authorize() must deny
Not:
Envelope does not exclude request
|
v
authorize() must allow
That distinction prevents an analytical projection from becoming a second authorization path.
The security model
The deeper principle behind the project is:
Evidence
|
v
Context
|
v
Authorization
|
v
Authority
Not:
Evidence
|
v
Authority
A signature is evidence.
A risk score is evidence.
A monitoring result is evidence.
An AI-generated assessment is evidence.
None of them should automatically become authority.
And the tests?
v2.5 has 16 named security invariants covering properties such as:
AUTHORIZATION_UNIQUENESS
MODEL_NON_AUTHORITY
CONTROL_PLANE_INTEGRITY
PROVENANCE_INTEGRITY
FAIL_CLOSED
ENVELOPE_SOUNDNESS
DELEGATION_MONOTONICITY
CAPABILITY_MONOTONICITY
REVOCATION_MONOTONICITY
POLICY_NON_WIDENING
AEGIS_STATE_TRANSITIONS
REVALIDATION_CONSISTENCY
The interesting rule is that:
UNVERIFIABLE != PASS
If an invariant cannot establish its claim, it does not quietly report success.
That is a small implementation detail with a large security consequence.
What v2.5 taught me
The hardest bugs weren't always sophisticated attacks.
Some were caused by something much simpler:
unknown
|
v
nothing obviously wrong
|
v
allow
That is the dangerous direction.
The safer model is:
unknown
|
v
cannot establish
|
v
deny / restrict / revalidate
Agent Firewall v2.5 is therefore less about adding another security feature and more about making the existing boundary harder to escape.
The goal remains simple:
AI agents can have powerful capabilities. Those capabilities should be explicit, bounded, and enforceable.
Top comments (0)