DEV Community

AI Agent Governance on AWS: Block Agents, Prove EU AI Act Compliance

Sarvar Nadaf on September 29, 2026

Every fact here is verified against an actual run (Amazon Nova Pro us-east-1 on-demand, Strands Agents, and traccia 0.1.29 $0.0008 per 1K input an...
Collapse
 
mrsaynothing profile image
Mr Say Nothing •

Two of three policies blocking nothing is the honest result most governance posts skip. The question your data raises: does a control that has never fired stay in the policy set as insurance, or does it become audit theater you pay compute for? And did the one that did fire catch something a plain allowlist would have missed?

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

the two that blocked nothing werent stale or misconfigured. the signal just wasnt in the spans. spend cap had near zero cost to trip and model boundary needs an auto patched llm client which a strands bedrock model isnt. so they structurally cant match this crew. not insurance not theater just the wrong control for what the workload emits.

and yeah for this crew a plain allowlist does the same job. the loop cap only earns its place on the runaway case where every tool is allowed but the agent calls the allowed one in a loop. an allowlist says yes every time. the cap counts. simpler tool set use the allowlist the cap is for when the danger isnt which tool its how many times

Collapse
 
rudratosh profile image
Rudratosh Shastri •

Hard-blocking a runaway agent + PII redaction across sub-agents is exactly the layer people skip because the demo works without it.

The detail I'd underline: the control has to live at the tool-call boundary, not in the prompt. A text-level "please don't do that" is a suggestion; a gate that inspects the actual call and its arguments is enforcement. An agent can be talked out of a rule in its context window — it can't be talked past a deny at the egress point.

For the EU AI Act audit trail — are you logging the blocked attempts too, or only the allowed calls? The denials are usually the more interesting evidence.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

exactly. the prompt is a suggestion the agent can be talked out of a deny at the call isnt in its context to argue with. thats why the real enforcement is the loop cap at the per call boundary not the injection text and why redaction is a span processor before export not an instruction.

and yes the denial is the record. the block becomes an incident with the decision id and trace id and thats what goes in the bundle article 12 wants the risky event logged not just clean ones. one wrinkle a hard blocked run has zero downstream spans by design so the proof is the decision id and incident not a fat span tree. allowed calls have the rich traces denied ones have the decision record. you need both and the denials are the more interesting half.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

🎥 𝗪𝗮𝘁𝗰𝗵 𝘁𝗵𝗲 𝗵𝗮𝗻𝗱𝘀-𝗼𝗻 𝗱𝗲𝗺𝗼

I walk through the AWS Strands multi-agent governance setup, runtime blocking, and the EU AI Act evidence flow.

👉 youtu.be/KO7IZ75rpqY?si=reDdwwwuzV...

Would love to hear your thoughts on AI agent governance in production. 🚀

Collapse
 
mateo_ruiz_6992b1fce47843 profile image
Mateo Ruiz •

The distinction between observability and governance is the part that really matters here: knowing that an agent did something wrong is fundamentally different from having a control point that can prevent the action.

The multi-agent identity issue is especially important. In production agent systems, it isn't enough to define a policy at the workflow entrypoint if the actual tool or model call is executed under a delegated agent identity. This is something we pay close attention to at IT Path Solutions when designing agent workflows: authorization and enforcement need to follow the capability that actually performs the consequential action.

I’d also separate detection, prevention, and evidence as three different guarantees. A post-run alert can establish that something happened, a runtime policy can prevent or terminate an action, and an immutable audit trail can establish what decision was made and why. Having one doesn't automatically give you the other two.

The fail-open detail is probably the most important operational caveat. A system can be “governed” on the happy path while still allowing a consequential call through during an enforcement-plane failure. For high-risk workflows, that failure mode needs to be an explicit architectural decision, not something discovered while debugging a demo.

The article's broader lesson is useful: governance isn't another observability dashboard; it's a control boundary in the request path.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

you named the exact spine of the piece and better than i did in places. the three guarantees being separate is the whole reason i split it into beats. a post run alert told me the agent leaked an email and approved something it shouldnt have. it could not stop either. the loop cap deny is a different guarantee entirely it happens before the call. and the audit bundle is a third thing again it says what got decided and why after the fact. having one gave me nothing toward the other two i had to build each on its own.

the delegated identity bit is the part that cost me an afternoon so it lands hard. i put the policy on the crew entrypoint the agent i wrapped with govern. it matched nothing. every per call check was attributed to the sub agent that actually made the tool calls not the entrypoint. because each sub agent runs under its own run_identity. so a rule scoped to the thing you think is in charge never fires the enforcement has to follow the identity that carries the consequential call exactly like you said. scope to the sub agent or org wide or you are governing a name nothing runs under.

and the fail open caveat yeah thats the one i least wanted to write and most needed to. setting fail_open=False only hardens the before run status check. the per call engine is always fail open with no override. so on the happy path it looks governed and a network blip mid run lets that one call through. for a high risk flow that cant be something you find while debugging a demo it has to be a decision you made on purpose with eyes open. governance in the request path not another dashboard is exactly it. thats the sentence i was reaching for

Collapse
 
steven_r_404 profile image
Steven Ray •

combination of AI agent governance, runtime enforcement, and audit evidence makes this especially relevant for teams building production AI systesm on aws cloud.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Yes it will definitely help building production ai systems on aws.

Collapse
 
salman_khan_c31307505285e profile image
Salmankhan •

This gonna assist better in production if we give it chance

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Yes for sure

Collapse
 
kyisaiah47 profile image
kyisaiah47 •

Does the spend policy aggregate cost across the parent run and delegated spans, or does each sub-agent receive its own budget?

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

good question, and it's the thing that tripped me up too. spend cap aggregates per run, not per sub-agent. there's no separate budget handed to each delegated agent. the platform reconciles cost against the trace/run id, so the parent run and everything it delegates roll up into one number that gets checked against the one ceiling.

the catch i hit: the sdk only supplies the signal for a spend cap on actual llm_call events (it sends remaining_budget_usd, input tokens, model, then settles reserved vs actual after). in this crew the model calls go through strands' bedrock client, which isn't an auto-patched client, so the spend cap had almost nothing to meter, and the tool spans themselves are basically $0. that's exactly why the spend cap denied nothing here and i had to reach for a loop cap instead, which counts tool calls per run.

so the mental model: budget is a run-level pool, aggregated across parent + delegated spans, enforced server-side against the trace id. if you want to catch
a specific sub-agent you don't give it its own budget, you scope the policy to that agent's identity (i scoped the loop cap to credit-risk because that's the run_identity active when the tools actually fire, not the entrypoint). happy to share the decision log screenshot if it helps.

Collapse
 
mustkhim_inamdar profile image
Mustkhim Inamdar •

Excellent work

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Thank You!

Collapse
 
normalnorma profile image
Norma •

Great write-up. The part that stuck with me most is the Decision Log diagnosis. Two policies returning "No Match" for structural reasons, rather than misconfiguration, is a failure mode that's nearly invisible without that screen. The lesson that a policy scoped to the entrypoint never matches because every per-call check is attributed to the sub-agent's run_identity is the kind of thing that costs teams days, and you're right that it isn't written down anywhere. "Governing a name nothing runs under" is a good way to put it.

I also appreciated how carefully you separated what the SDK does from what your own code does. Calling out that a Traccia guardrail detects but doesn't enforce, and that the Beat 1 block is your own control flow raising on a finding, is the sort of precision that makes the rest of the article credible. Same for showing Tiers A and C firing while saying plainly that Tier B didn't, instead of implying all three work.

The fail-open caveat deserves the weight you gave it. fail_open=False reads like a guarantee and only hardens the pre-run status check, while the per-call engine still fails open. For a high-risk credit-scoring flow, that has to be a deliberate architectural decision. One practical follow-up: since the platform can miss a call during an enforcement-plane failure, I'd pair it with an in-process fallback, like a local tool-call counter in your own code that raises independently of the network. It's crude, but it gives you a deny path that doesn't depend on the platform being reachable, and you can still use the platform for the decision record.

On the preventive vs. detective split, "Open Violations 0" next to real Denied rows is exactly the kind of dashboard moment that makes people think the tool is broken. Narrating that distinction up front saves a lot of confusion, and the hourly-cadence warning is a useful tip for anyone recording a demo.

A couple of questions and thoughts:

  1. Loop Cap tuning. A cap of 1 is perfect for a demo because the crew makes exactly two calls, but in production the threshold is the hard part. Too tight and you block legitimate runs, too loose and a loop burns budget before it trips. Did you look at deriving the cap from observed p95 or p99 tool-call counts per agent from your earlier runs? Part 1's per-agent data seems like a natural input.

  2. The evidence-integrity point. The unkeyed SHA-256 caveat is honest, and Sam's comment about external timestamping is worth considering. Since only a 32-byte digest needs to leave the boundary, anchoring the bundle hash with an RFC 3161 timestamp seems like a cheap addition that would strengthen the Article 12 story without touching any PII.

  3. Denials as evidence. The point in the thread that a hard-blocked run leaves almost no span tree, so the decision id and incident are the proof, is a good one. It might be worth adding a short note to the article on how to retain the decision record alongside the allowed-run traces, since a regulator will probably want both in the same view.

The takeaway that watching a failure and stopping one are different layers is the right one, and the "instrument first, write policies second" lesson from the comments follows directly from your findings. Thanks for publishing the repo and the unit tests. Being able to reproduce the $0 beats without a key makes this much easier to learn from.

Collapse
 
slabb profile image
Sam LABBE •

The caveat most governance writeups never write is the one you did: "integrity_hash is a plain unkeyed SHA-256... anyone who can rewrite the bundle can recompute the hash." That sentence deserves the next rung of the ladder, because it stops one step before the real fix.

The ladder runs: a hash detects accidents, a signature attributes them to a key — and neither stops the key holder. Anyone who can rewrite the bundle can also re-sign it; a regenerated history verifies against its own key with flying colors, and a seal created after the fact tells you nothing about what existed before. "When" is the property that survives an incident, and it can't come from inside the system the record describes.

The standard pieces are cheap: hand the digest — 32 bytes, nothing else — to witnesses outside your trust boundary. An RFC 3161 timestamp from a qualified TSA carries a presumption of correctness under eIDAS (the tier a regulator's lawyer accepts), and Bitcoin anchoring via OpenTimestamps gives you the tamper-proof backstop. Only an opaque digest ever leaves, which slots neatly into the PII redaction story you already tell: the GDPR conversation stays short.

Which is the one thing your evidence section doesn't answer yet: the pack proves what Traccia recorded, but "we have logs" and "we can prove these logs were never touched" are two different sentences to an authority — and Article 12's whole point is the second one. The Governance Hub is operator-owned infrastructure, so the platform that records is the platform that could rewrite. Who witnesses the witness?

Curious whether the export carries any externally checkable time today, or whether that's the missing beat.

Collapse
 
onizuka profile image
Onizuka •

The part about two policies structurally matching nothing is the real finding here. I hit the same wall building guardrails on LangGraph — policies written for one agent topology silently do nothing when the actual call graph doesn't produce the spans they expect. The expensive lesson: instrument first, write policies second, because you can't govern a signal you've never seen in the traces.

Collapse
 
kielltampubolon profile image
Kiell Tampubolon •

The prompt injection hard-block before any model call is the part I'd want more detail on. Manifest and tool-description level injection checks are where I've found the most false negatives in MCP tooling: attackers don't need to touch the model at all if they can poison what it's told about its own tools. Is the block pattern-based, or is Traccia doing something closer to intent classification? Curious what the false-positive rate looks like on legitimate edge-case prompts.

Collapse
 
mudassirworks profile image
Mudassir Khan •

the distinction between observability and governance is one teams consistently blur until a regulator asks for paperwork. observability says 'a runaway agent approved a loan it should not have approved.' governance says 'that call never reached the model.' the part about two policies blocking nothing because they had no signal to match in the crew is the most honest framing I have seen on guardrail design — most posts skip that. what format does the exported EU AI Act evidence take: structured spans that map to Annex III directly, or something a human auditor has to interpret?