Skip to content

AgentGuard v1.1.0

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 14 Sep 00:21
· 318 commits to master since this release

Minor, not patch — this release adds public API. The exported Go surface goes from 525 identifiers to 549: 24 added, 0 removed, so the 1.x additive-only guarantee in docs/COMPATIBILITY.md holds as measured rather than as asserted.

What's in it

Eighteen fixes, from three different places:

  • Six from auditing the packages no prior review had opened — pkg/metrics, pkg/depaudit, pkg/migrate, cmd/agentguard, and both SDKs. Two were behaviours the docs described that the code never implemented; a third meant no production deployment had ever written a replay checkpoint, so every boot re-scanned the whole audit log and decision counters restarted from zero.
  • Five LLM-proxy gating fixes (B6, B7, B17, B18, B21) — each one a stream or response the firewall could not evaluate and forwarded, dropped, or wrongly refused anyway.
  • Five that only a running cluster could surface — a migration race that killed replicas at boot, a rotation that destroyed archives, a flush deadlock between nodes, streaming refusals that reached the client but never the audit trail, and a checkpoint path nothing had ever written to.

Compatibility

Additive-only vs 1.0.0: no exported identifier, config key, route, or wire field was renamed, removed, or re-signatured.

One deliberate wire-behaviour change: an OpenAI stream returning tool calls on more than one choice (n > 1) is now refused under deny:llm_api_proxy:multi_choice_tool_calls instead of being silently merged. That merge was never correct — the argument string it evaluated belonged to no real call, while the actual calls reached the client unexamined. n = 1, what every mainstream agent framework sends, is byte-identical to 1.0.0.

Counters step up once on the first boot after upgrading: they now report lifetime totals rather than "since the previous boot". Rate-based alerts are unaffected.

Performance

The /v1/check hot path is untouched and sits at its recorded baselines — ratelimit.Allow 0 allocs, policy.Engine.Check 6 allocs. The streaming accumulators are touched, and were measured against the pre-change tree in a worktree rather than assumed: identical allocations on all four paths, timing differences inside run-to-run noise.

Verification

On the shipped image, in a three-node PostgreSQL cluster:

  • ~4.9M requests across three load shapes, zero errors, zero deadlocks
  • Concurrent hot-path p99 0.562 ms against a 3 ms budget, 153,299 samples
  • A node SIGKILLed under load: survivors kept serving, and the killed node restored its exhausted rate-limit state from Postgres on restart
  • Cross-node one-shot approval consumption — approved on one node, allowed once on another, refused everywhere after
  • All five adversarial upstreams refused with a matching central audit entry and no tool call reaching the client
  • Accumulators fuzzed for 17.9M executions with no crashes
  • CI 19/19 green

Full detail, including every fix and the reasoning behind the wire-behaviour change, is in CHANGELOG.md.