| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| agentguard-v1.0.0.cdx.json | 2026-07-22 | 18.5 kB | |
| agentguard-v1.0.0.spdx.json | 2026-07-22 | 28.4 kB | |
| lictorate_s_AgentGuard_v1.0.0 source code.tar.gz | 2026-07-22 | 960.3 kB | |
| lictorate_s_AgentGuard_v1.0.0 source code.zip | 2026-07-22 | 1.2 MB | |
| README.md | 2026-07-22 | 12.1 kB | |
| Totals: 5 Items | 2.2 MB | 0 | |
AgentGuard 1.0.0 — Release Notes
Released: 2026-07-21
Headline: The validated 1.0 — multi-node, and the additive-only guarantee goes hard. AgentGuard now runs as more than one replica against a shared PostgreSQL backend, with approval / rate-limit / cost state converging across nodes through a background reconcile loop — without touching the /v1/check hot path (no synchronous DB call added; the <3 ms p99 budget is preserved and gated in CI). From 1.0, the additive-only rule in COMPATIBILITY.md is a hard guarantee for the entire 1.x line, not an intent.
The self-hosted Apache-2.0 build remains fully featured. The hosted, multi-tenant version (AgentGuard Cloud) lives at https://agentguard.lictorate.com.
A single-node deployment upgrades as a binary/SDK swap. Multi-node is entirely opt-in behind a Postgres
--store-dsn. If you runreplicas: 1today, nothing about your topology changes.
Why this release exists
v0.9 stabilized and enumerated the public surface; 1.0 is the release that makes the promise real and then locks it:
- Multi-node became a shipped capability, not a roadmap item. AgentGuard can now run as several replicas over a shared PostgreSQL store, with approvals, rate-limit consumption, and cost accumulators converging across nodes — and it does so off the hot path, so the latency budget that defines the product is untouched.
- The additive-only guarantee goes from intent to hard guarantee. The pre-1.0 validation window is over. For the 1.x line, no frozen field, route, flag, subcommand, scope, or schema version is removed, renamed, or repurposed — a genuinely breaking change would be a major-version event behind a new identifier, never a silent mutation.
- The security, validation-hardening, and doc-honesty work done on the road to 1.0 lands with it — three enforcement fixes (F1–F3), three dependency/toolchain advisories closed, audit-worker crash isolation, and a documentation-accuracy pass so the shipped behavior and the docs agree.
What changed
PostgreSQL multi-node backend — shared state across replicas
A Postgres --store-dsn (postgres://… / postgresql://…) selects a new store.PostgresStore (pgx v5 via the database/sql stdlib driver, pure-Go, mirroring SQLiteStore) behind the existing frozen store.Store interface. Approvals, rate-limit consumption, and cost accumulators are the shared durable tier; each node keeps its in-memory state authoritative and converges in the background via per-node-rows-summed consumption tables and interval approval re-hydrate + merge. The zero-config single-node SQLite default is unchanged — the reconcile ticker never starts on SQLite.
New opt-in serve flags:
--node-id(default: OS hostname) — stable identifier for this node in reconciliation. Each replica must have a distinct value; an empty value disables reconciliation.--reconcile-interval(default2s) — cadence of the background reconciliation loop. Takes effect only with a Postgres--store-dsn; on SQLite it is forced to0and the loop never starts.--approval-validity(default5m) — how long a resolved ALLOW is honored by the/v1/checkapproval-id retry, measured from resolution.0restores the unbounded pre-1.0 window. The default matches the SDKs'wait_for_approvalpoll window.
Design: v1.0-multinode-PLAN.md. Operator sizing: OPERATIONS.md.
Distributed rate limiting is bounded-overshoot by design — read this before scaling out
Because reconciliation is background-only (to hold the no-sync-DB-on-/v1/check invariant), a cluster's aggregate admissions may exceed a single global budget by up to ≈ reconcile-interval × peak-rate × replicas before convergence. This is a deliberate, documented trade: global-strict limiting would require a synchronous cross-node check on the hot path, which the latency budget forbids. Single-node behavior is exactly as before. See COMPATIBILITY.md and set --reconcile-interval with the sizing note in mind.
Approval lifecycle hardening — enforced cluster-wide
Resolutions are now write-once: re-approving an approved id (or re-denying a denied one) stays an idempotent no-op, but a conflicting re-resolution returns 409 Conflict with a structured body instead of silently flipping the decision — the last-write-wins hole is closed. A resolved ALLOW is one-shot and time-boxed: the first /v1/check retry carrying the approval_id consumes it, later replays re-enter the approval flow, and honoring is bounded by --approval-validity. Across nodes a monotonic merge preserves these properties (DENY-wins, never un-resolves, never clears consumed_at), so one human click is honored once per cluster and a Postgres restart never resurrects a spent ALLOW.
Central audit-ingest endpoint + forced-DENY audit fidelity (F1)
New auth-gated, DENY-only POST /v1/audit (+ /v1/t/{tenant}/audit). It backs the F1 fix: when a proxy forces a fail-closed refusal for a malformed streaming tool call, it now records the forced malformed_tool_call DENY centrally instead of letting the policy verdict stand in the audit trail. On a record failure (canonical case: a keyed central server but an unkeyed proxy → 401), the proxy best-effort falls back to the /v1/check audit path — fidelity degrades to the engine verdict but coverage does not. Give the proxy --api-key when the central server is keyed for full fidelity.
Enforcement fixes
- F1 — malformed streaming tool calls now fail closed. A completed tool call whose assembled arguments weren't valid JSON previously skipped the gating switch entirely (no refusal, no audit entry, and the accumulator stayed broken for the rest of the connection). Both providers now emit a fail-closed synthetic refusal, reset the accumulator, and continue. The happy path is byte-identical — only the malformed-completion branch changed; the
Allowpath stays 0-alloc. - F3 —
globMatchdocumentation corrected + non-fatal policy-lint warning. A path pattern with/but no**(e.g./workspace/*) matches across/(so/workspace/a/b/secret.envmatches). Matching semantics are unchanged (tightening them would break the frozen policy contract); the contract doc is corrected and policy load now emits a non-breaking lint warning for patterns that match more broadly than they appear to. Bound single-directory allows with an explicit**-anchored pattern. - Python SDK filesystem-
actioninference now matches the Go transports. The adapters inferred the filesystemactionby substring with a narrower verb set, so several verbs sent noaction(andset_targetmis-fired on "get"). Inference is now the same prefix-matched verb groups asgateclient.InferFilesystemAction, pinned by a parity test. Operators whose policies key on filesystemactionmay see new (correct) matches through the Python adapters. - Audit flush workers now isolate panics from the underlying logger. A panic inside
underlying.Logpreviously escaped the buffered-audit worker unrecovered and aborted the process, violating "a flush worker crash must never block or crash the proxy loop." The worker now recovers the panic and routes the entry to the durable overflow path instead of crashing.
Security
- F2 — domain matching is now case-insensitive (was a true fail-open deny bypass). A deny rule for
evil.comdid not match the DNS-equivalent requestEVIL.com; the deny was skipped and the request fell through to allow. Both the request domain and rule domains are now lowercased. No measurable hot-path cost. - pgx v5.9.1 → v5.9.2 — GHSA-j88v-2chj-qfwx (SQL injection) via dollar-quoted placeholder confusion, reachable through the new Postgres backend. Both the
govulncheckjob and the cross-ecosystemdep-auditgate fired correctly; verified clean live against OSV.dev post-bump. - golang.org/x/text v0.29.0 → v0.39.0 — GO-2026-5970 (infinite loop on invalid input), reachable via pgx in
pkg/store/postgres.go. Cleared bothgovulncheckand the live OSV.devdep-auditgate. - crypto/tls Encrypted Client Hello privacy leak — GO-2026-5856, resolved by pinning the build toolchain to
go1.25.12(go1.25.11 and earlier trip the CIgovulncheckgate).
Documentation & metadata
- MCP Gateway protocol-coverage docs truthed up (no behavior change). The gateway brokers
tools/*only —resources/*andprompts/*are masked (MethodNotFound), not "forwarded verbatim" as the old docs claimed;notifications/cancelledis documented as a latent no-op. Dropped server-initiated frames carrying amethodnow log at Info (was Debug) so operators see unsupported server→client requests. - v1.0 documentation-accuracy pass across README, COMPATIBILITY, MIGRATION, and the current-state docs — describing the shipped multi-node behavior, bounded-overshoot semantics, the approval write-once/one-shot lifecycle, and F1–F3.
- Version set to 1.0.0 across all three binaries (
agentguard,agentguard-mcp-gateway,agentguard-llm-proxy) and both SDKs (agentguardproxyon PyPI,@agentguard/sdkon npm).
Breaking changes
One, made under the pre-1.0 reserved right: approval resolutions became write-once / one-shot (see Approval lifecycle hardening). This is the last-write-wins → write-once tightening documented in MIGRATION.md, and it is the reason the freeze is anchored at 1.0 rather than 0.9.
Otherwise this release is additive-only vs v0.9.0 — verified by diffing go doc -short ./... against the v0.9.0 tag: 0 exported declarations removed, and the only CLI-flag changes are the three additions above. The /v1/check request/response shapes, the audit format (schema_version: 2), and the policy schema (version: "1") are byte-for-byte compatible with v0.9.
Upgrade notes
- Single-node is a binary/SDK swap — no config or data changes required. On first boot the SQLite/Postgres store applies an additive, idempotent schema migration (the
approvalstable gainsconsumed_at/resolved_via/resolved_from; therate_consumptionandcost_consumptiontables are created); pre-1.0 approvals load as unconsumed and no rows are rewritten.agentguard migrateis not involved. - Multi-node is opt-in. Provision PostgreSQL, set
--store-dsn postgres://…and a distinct--node-idper replica, and read the bounded-overshoot sizing note before setting--reconcile-interval. - Rollback to v0.9.0 is supported (the extra columns/tables are ignored by the older binary), with two documented degradations. Full detail:
MIGRATION.md. - Go installers (
go install): pull the v1.0.0 tag (below). - Docker:
docker pullagainst the next published image after this release. - Python SDK:
pip install --upgrade "agentguardproxy==1.0.0". - TypeScript SDK:
npm install @agentguard/sdk@1.0.0.
Get started
:::bash
# Install all three binaries at the v1.0.0 tag
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard@v1.0.0
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard-mcp-gateway@v1.0.0
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard-llm-proxy@v1.0.0
# Or upgrade the SDKs only
pip install --upgrade "agentguardproxy==1.0.0"
npm install @agentguard/sdk@1.0.0
Full change list: CHANGELOG.md § 1.0.0. Stabilized surfaces and the additive-only guarantee: COMPATIBILITY.md. Multi-node design: v1.0-multinode-PLAN.md.