Repository navigation
Releases: lictorate/vanzik
Release list
Vanzik v2.0.0
Vanzik 2.0.0 — Release Notes
Released: 2026-10-26
Headline: AgentGuard is now Vanzik. Same guard, new name, and upgrading moves your install for you.
Check these before you upgrade.
- Every name that said AgentGuard says Vanzik, with no alias for the old one: programs,
VANZIK_*environment variables, folders, login services, headers, metrics and packages.- An
AGENTGUARD_*variable that would be used now stops the program instead of being ignored, so a server never runs without the API key its operator configured.- 1.x's in-place update (
agentguard setup→ Update) can't install 2.0; use the 2.0 installer. SeeMIGRATION.md§ v1.x to v2.0.
Why this release exists
2.0 renames the project, and the rename, including refusing the old AGENTGUARD_* variable names, is its only breaking change. Everything else is an addition (vanzik migrate --from-agentguard, the server adopting an agentguard.db) or a fix. The policy schema, the /v1/* wire protocol, the audit log format and seal, the store and every command's existing flags are as in 1.4.0, so policies, audit logs and databases carry over unchanged.
A rename is risky in one place: the coding-agent hooks AgentGuard connected run the agentguard program, and most agents let a tool call through when a hook's program is missing. So 2.0 doesn't leave the move to you.
What's new
Upgrading moves an existing install: vanzik migrate --from-agentguard
The 2.0 installers run it after installing, and vanzik setup runs it before its menu. It:
- stops and removes AgentGuard's login service first, and changes nothing if it can't;
- moves the config and data folders by renaming them, so the policy, API key, audit log and seal key keep their bytes;
- renames
agentguard.dbtovanzik.dband updatessetup.json's paths; - points the Claude Code, Cursor, Gemini CLI and Codex hooks AgentGuard wrote at
vanzik, keeping--fail-closed, your own hooks, and a.vanzik-backupof every file it edits; - installs the login service again under the new name and waits for the server;
- lists what is left to do by hand: trusting the changed hook in Codex,
AGENTGUARD_*variables, MCP client configs that start the old gateway, the old programs.
vanzik server also renames an agentguard.db it finds in its --data-dir instead of starting on an empty database.
Breaking changes
| 1.x | 2.0 | |
|---|---|---|
| Programs | agentguard, agentguard-mcp-gateway, agentguard-llm-proxy |
vanzik, vanzik-mcp-gateway, vanzik-llm-proxy |
| Environment | AGENTGUARD_* |
VANZIK_* |
| Docker image | ghcr.io/caua-ferraz/agentguard |
ghcr.io/lictorate/vanzik |
| Go module | github.com/Caua-ferraz/AgentGuard |
lictorate.com/vanzik/v2 |
| Python SDK | agentguardproxy |
vanzik |
| TypeScript SDK | @lictorate/agentguard |
@lictorate/vanzik |
| Repository | github.com/Caua-ferraz/AgentGuard |
github.com/lictorate/vanzik |
Folders, login services, headers (X-Vanzik-*), metrics (vanzik_*), cookies and the rest are in the full table in MIGRATION.md.
Other fixes
- Agent settings files saved with a UTF-8 byte order mark (as Notepad does) can be connected, disconnected and migrated; they were refused as invalid JSON.
Get started
curl -fsSL https://github.com/lictorate/vanzik/releases/latest/download/install.sh | sh
vanzik setup
# or
docker run -d -p 8080:8080 -e VANZIK_API_KEY="$KEY" \
-v vanzik-audit:/var/lib/vanzik ghcr.io/lictorate/vanzik:2.0.0
go install lictorate.com/vanzik/v2/cmd/vanzik@v2.0.0
pip install "vanzik==2.0.0"
npm install @lictorate/vanzik@2.0.0Releases before 2.0 stay on PyPI as agentguardproxy and on npm as @lictorate/agentguard.
Full change list: CHANGELOG.md § 2.0.0.
AgentGuard v1.4.0
AgentGuard 1.4.0 — Release Notes
Released: 2026-10-15
Headline: Guard every coding agent, prove the audit log wasn't touched, and test your policy like code.
Check these before you upgrade.
- A
require_priordeny or approval rule now fires when AgentGuard can't read the history it depends on; before, a lookup error quietly skipped it.- Shell rules now see commands hidden inside
bash -c,evaland wrappers such astimeoutorenv, so some commands that used to pass now ask for approval.GET /v1/auditnow answers 400 to asinceoruntilthat isn't an RFC 3339 time, where 1.3 ignored both parameters. SeeMIGRATION.md.
Why this release exists
1.3 made AgentGuard easy to install and put Claude Code behind it. 1.4 is about the questions people ask next:
- "We don't only use Claude Code." Cursor, Gemini CLI and Codex CLI are now guarded the same way. And if the AgentGuard server is down, you choose whether their calls go ahead or stop.
- "How do I know the audit log is real?" Sealing chains every entry to the previous one with an HMAC. Change, delete or reorder a line and
agentguard audit verifynames the exact place. - "How do I know my policy does what I think?"
agentguard testruns it against test cases in CI, including the raw tool calls your coding agents send. - "What did this agent and its subagents actually do?" Sessions tie the calls together, and the dashboard replays them step by step.
- "Can we hand this to an auditor?"
agentguard reportwrites the evidence for a period, mapped to SOC 2 criteria.
It also brings remote MCP servers to the MCP gateway, adds an OpenAI Agents SDK adapter, and fixes several ways a tool call could get past the policy unchecked. Two of them were found while preparing this release, and both affect 1.3: commands hidden inside bash -c or eval, and disguised spellings of a blocked address.
What's new
Cursor, Gemini CLI and Codex CLI
agentguard setup can now connect Cursor, Gemini CLI and Codex CLI as well as Claude Code. Each one gets a hook that checks its tool calls on your AgentGuard server before they run:
| Agent | Checked |
|---|---|
| Claude Code | shell, file reads and edits, web fetches, MCP tools |
| Cursor | shell, file reads and writes, MCP tools |
| Gemini CLI | shell, file reads and writes, web_fetch (every URL in the request), MCP tools |
| Codex CLI | shell, apply_patch (every file the patch touches), MCP tools |
Allowed calls carry on under the agent's own permissions. Denied calls are blocked and the agent sees why. Calls that need approval wait up to 5 minutes for you in the dashboard or with agentguard approve. When one action touches several things, such as a patch that edits three files, each one is checked and the strictest answer wins. Gemini's web_fetch URLs are read the way Gemini itself parses them, so a disguised host can't slip past a network rule.
Fail open or fail closed. When connecting an agent, setup asks what should happen if AgentGuard isn't running:
- Let calls through with a warning: the default, as in 1.3.
- Block them (
agentguard hook <agent> --fail-closed): an unreachable server, a rejected API key or an unreadable call is denied with ruledeny:hook:fail_closed.
Each agent has a ready-made rule block (configs/cursor.yaml, configs/gemini-cli.yaml, configs/codex.yaml), and setup offers to add it to your policy. The audit log and dashboard tag every call with its agent (claude_code, cursor, gemini_cli, codex_cli). See CODING_AGENTS.md.
A tamper-evident audit log
agentguard server --audit-seal --audit-seal-key-file /var/lib/agentguard/audit-seal.key
agentguard audit verify --audit-log /var/lib/agentguard/audit.jsonl \
--key-file /var/lib/agentguard/audit-seal.key-
The chain. With
--audit-seal, every entry carries a sequence number, the previous entry's hash and its own HMAC-SHA256. The chain also links across rotated and gzipped files. -
Verifying it.
agentguard audit verifychecks it offline. Edit a byte, delete a line, swap two lines or cut the file short, and it exits 1 with the file and line where the chain breaks. A log with no sealed entries gets "NOT SEALED" and exit 1 too, since nothing could be verified. -
Anchors. AgentGuard publishes the head of the chain every 5 minutes and on every rotation, in three places:
- a log line, which ends up in journald, syslog or your SIEM;
- a Prometheus gauge;
GET /v1/audit/head.
Pass one to
audit verify --anchorand even someone holding the key can't rewrite history before it. -
New installs are sealed by default.
agentguard setupgenerates a key that only you can read. -
Off unless you turn it on otherwise. Existing installs keep byte-identical logs until you turn sealing on. The audit format version is unchanged.
Policies as code: agentguard test
# policy_test.yaml
policy: policy.yaml # yours, with the Codex block setup adds
tests:
- name: plain ls is allowed
request: {scope: shell, command: "ls -la"}
expect: ALLOW
- name: Codex can't patch an .env file
hook: codex
input: {cwd: /home/dev/app, tool_name: apply_patch,
tool_input: {command: "*** Begin Patch\n*** Update File: .env\n..."}}
expect: REQUIRE_APPROVALagentguard test runs every case against your policy. Each case starts fresh, so earlier cases can't change later ones. A case can seed the history a require_prior rule needs, or give a coding agent's raw tool call through hook:, which goes through the same mapping as the real hook.
The command fits CI: --output junit for test reports, --run to filter cases, and exit 1 when anything fails. AgentGuard's own CI runs 158 cases against the shipped policies. See POLICY_REFERENCE.md § Testing policies.
Sessions and replay
- Parent and child sessions. A request can now name the session that started it (
parent_session_id). It can be set in four places:- the Python SDK:
Guard(parent_session_id=...); - the TypeScript SDK:
parentSessionId; - the LLM proxy: the
X-AgentGuard-Parent-Sessionheader; - the hooks, which also record which subagent made each call.
- the Python SDK:
- Session API.
GET /v1/sessionslists sessions with their agents, subagents, transports and counts by decision.GET /v1/sessions/{id}returns one session's calls and its child sessions. - Audit filters.
GET /v1/auditandagentguard auditgainsince,until, session and parent-session filters.
The dashboard has a new Sessions tab. Pick a session and replay it with one lane per agent and subagent:
- Controls: play, pause, step, back and 1x–50x speed.
- Approvals appear next to the call they unblocked.
- Parent and child sessions link to each other.
#session=<id>links can be shared.
Compliance evidence: agentguard report
agentguard report --since 7d --output html --out-file week.htmlThe report covers one period. It comes as Markdown, JSON or a single HTML page, and contains:
- the policy in force, identified by SHA-256;
- whether the audit log verified;
- decisions by type, scope, agent and transport;
- the rules that denied or held the most calls;
- approvals;
- the sessions and agents seen;
- posture checks, such as sealing on and secrets masked.
A section maps this evidence to SOC 2 criteria CC6.1, CC6.6, CC7.2, CC7.3 and CC8.1. It is labelled as supporting evidence for an auditor, not a certification.
It reads the audit log files, rotated ones included, and verifies them. Or it can read a running server with --url. It exits 1, still writing the report, if the audit log fails verification. See COMPLIANCE.md.
Remote MCP servers in the MCP gateway
agentguard-mcp-gateway \
--upstream-http "docs:https://mcp.example.com/mcp" \
--upstream-header 'docs:Authorization=Bearer ${DOCS_TOKEN}'- Remote servers. The gateway can now guard MCP servers that speak Streamable HTTP, alongside the ones it starts locally.
- Secrets. Header values can come from environment variables, so tokens stay out of client configs and logs.
- Recovery. Expired sessions are renewed. A server that goes away is retried until it comes back, and the host is told to refresh its tool list.
- Protocol. MCP
2025-06-18is supported.
OpenAI Agents SDK adapter
from agents import Agent
from agentguard import Guard
from agentguard.adapters.openai_agents import guard_tools
agent = Agent(name="ops", tools=guard_tools([run_shell, read_file], Guard()))guard_tool and guard_tools check each tool call before it runs, and agentguard_tool_guardrail plugs into the SDK's own guardrails. A denied call raises AgentGuardDenied, and the tool never runs. Install it with pip install "agentguardproxy[openai-agents]"; it supports openai-agents 0.3.3 up to 1.0.
Security fixes
- A command hidden inside another shell skipped the shell rules.
bash -c 'rm -rf /',eval rm -rf /,timeout 5 bash -c '…'andenv sudo …each read as one harmless-looking command. Under the coding-agent blocks, which allow everything except what they deny or send to approval, they passed without the approvalrm -rfandsudoneed. This has affected Claude Code since 1.3.- AgentGuard now checks the commands inside:
sh/bash/zshand other shells'-cscripts;eval, and here-s...
- AgentGuard now checks the commands inside:
AgentGuard v1.3.0
AgentGuard 1.3.0 — Release Notes
Released: 2026-09-26
Headline: Install AgentGuard with one command, set it up from a menu, and guard Claude Code.
Using the MCP gateway? 1.3.0 refuses to start when its command line has a stray word. If an MCP client config splits an
--upstreamcommand across severalargsentries, put it back in one string: 1.2.0 started that MCP server without its arguments. SeeMIGRATION.md.
Why this release exists
Getting AgentGuard running used to mean a Go toolchain, a policy file in the right folder, and a server command you had to remember. This release makes the first ten minutes work:
- Install without Go. Every release has prebuilt binaries for Linux, macOS and Windows (amd64 and arm64), one-line installers that check them against the release's checksums, and a container image.
- Set it up from a menu.
agentguard setupdoes the rest and can undo it. - Guard the agent people actually use. Claude Code's tool calls go through your policy.
- A command line that forgives. A review found flags silently ignored, arguments silently dropped, and help that exited as an error. They're fixed.
What changed
Install, update, uninstall
# Linux / macOS
curl -fsSL https://github.com/Caua-ferraz/AgentGuard/releases/latest/download/install.sh | sh
# Windows (PowerShell)
irm https://github.com/Caua-ferraz/AgentGuard/releases/latest/download/install.ps1 | iexThe installers put all three binaries and a starter policy in place. Rerunning one updates and says what changed (Updated, Reinstalled, Downgraded with a warning); --uninstall / AGENTGUARD_UNINSTALL=1 removes AgentGuard and keeps your policy unless you add --purge. The container image is ghcr.io/caua-ferraz/agentguard, for amd64 and arm64. A smoke test installs the published release on seven platforms after every release.
agentguard setup
A menu — arrow keys and Enter, no flags. The first run asks two questions (start at login? API key?) and then writes the starter policy, creates a data folder for the audit log and database, saves an API key only you can read, and installs a login service that starts the server: a systemd user service, a LaunchAgent or a Task Scheduler task, with no admin rights. Later runs open the dashboard (with the key on your clipboard for its login), update in place, restart the server, show the settings for the SDKs and Claude Desktop, connect Claude Code, and — last on the list, after offering to just stop the server — uninstall.
Claude Code
Connect Claude Code in agentguard setup adds a hook to Claude Code's settings. Before each shell command, file read or edit, web fetch or MCP tool call, Claude Code asks AgentGuard:
| AgentGuard | Claude Code |
|---|---|
| ALLOW | carries on; its own permission prompts still apply |
| DENY | blocks the call, and Claude sees the reason |
| REQUIRE_APPROVAL | waits up to 5 minutes for an approval in the dashboard or with agentguard approve |
| server not running | carries on, with a warning that the call wasn't checked |
The starter policy has rules for Claude Code: your projects are open, credentials and system folders aren't, and destructive commands (sudo, rm -rf, force-push, git reset --hard, curl … | sh) and .env files need approval. See CLAUDE_CODE.md.
The command line
agentguard serveris the command's name;servestill works.- Flags work before or after arguments.
agentguard approve ap_1 --url Xused to approve on localhost. agentguard validate policy.yamlvalidates that file; it used to validate the default and print VALID.- Typos get suggestions (
--serve→ Did you mean 'agentguard server'?), stray words are errors, and help exits 0 with examples on every page. - The policy file is found in
AGENTGUARD_POLICYor the installer's folder when--policyisn't given;AGENTGUARD_URLworks for the CLI, the gateway and the proxy. - Errors say what to do, e.g. Cannot connect to AgentGuard at … Is 'agentguard server' running there?
Also
agentguard server --api-key-filekeeps the key out of command lines and service definitions.- The update notice names the command that updates (
agentguard setup,docker pullorgo install). - The MCP gateway applies
--reconnect-cap, which it used to ignore. - The LLM proxy recognizes Claude Code's tool names.
Breaking changes
None in the public API: no field, route, flag, subcommand or schema version was removed or changed, and policies decide as they did in 1.2.0.
Command lines that used to be half-ignored now act fully:
- A flag after an argument applies, and a stray argument is an error — also for the MCP gateway and LLM proxy.
validate <file>validates that file.- Command-line mistakes exit 2; help exits 0.
AGENTGUARD_URLand the keyagentguard setupsaves are used when no flag gives a server or key.
Upgrade notes
- Check scripts and MCP client configs for the command-line changes above.
- Rerun the one-line installer, or
go install github.com/Caua-ferraz/AgentGuard/cmd/...@v1.3.0. Nothing on disk changes format. - Run
agentguard setupif you want AgentGuard to start at login or to guard Claude Code.
Details: MIGRATION.md § v1.2.x → v1.3.0.
Get started
curl -fsSL https://github.com/Caua-ferraz/AgentGuard/releases/latest/download/install.sh | sh
agentguard setup
# or
docker run -d -p 8080:8080 -e AGENTGUARD_API_KEY="$KEY" \
-v agentguard-audit:/var/lib/agentguard ghcr.io/caua-ferraz/agentguard:1.3.0
pip install --upgrade "agentguardproxy==1.3.0"
npm install @lictorate/agentguard@1.3.0Full change list: CHANGELOG.md § 1.3.0.
AgentGuard v1.2.0
AgentGuard 1.2.0 — Release Notes
Released: 2026-09-24
Headline: A security release. Shell allow rules no longer let chained commands through, a second rule block for a scope is no longer ignored, and secrets are masked in the audit trail by default.
If your policy allows shell commands with patterns like
ls *orgit *, 1.1.x allowed agents to chain any command after them (ls /tmp; rm -rf /), past your deny and approval rules. Upgrade, then check your policy withagentguard validate --strictand the replay recipe inMIGRATION.md.
Why this release exists
1.1.1 was tested end to end on a local machine, not only in CI. The test ran every surface in a sandbox: the CLI, the server, approvals, audit, persistence, multi-tenant and multi-node PostgreSQL deployments, the MCP gateway, the LLM proxy, both SDKs and the dashboard. It sent spam, floods, malformed input and bypass attempts. It found:
- A policy bypass. A shell rule's glob matched the whole command line, and one
*matched every shell operator in it. With the shipped default policy,ls /tmp; rm -rf /,git clone x && sudo rm -rf /,ls $(rm -rf ~)andecho x > /etc/passwdwere all allowed, getting past itssudoandrm -rfapproval rules. - A second rule block for a scope was ignored whenever the first one decided, so a separate
denyblock could silently do nothing, andvalidatesaid the policy was valid. - Secrets were stored verbatim in the audit trail and shown by the audit API, the live stream and the dashboard.
pip install agentguardproxy[all]installed CrewAI 0.193, on which a crew ran its tools without a single policy check.- Rerunning the test against the fixes found one more bypass: the MCP gateway checked a model-supplied
domainargument instead of the host of the URL the tool would fetch. - Six more medium-severity problems, among them: URL-only network checks ignored the URL; the lifetime decision counters lost the counts of pruned audit archives;
agentguard auditshowed the oldest entries instead of the newest; three MCP file-write tools passed the default policy; and the custom-tool snippet in two guides made the policy fail to load.
What changed
Shell commands are checked one command at a time
A command that contains shell syntax (;, &&, ||, |, &, a line break, $( ), backticks, a redirection) is split into the simple commands a shell would run, and each one must pass your rules; the most severe verdict wins. Redirection targets are checked against your filesystem rules. Whole-command deny and approval rules still run first, and a command with no shell syntax is matched exactly as before.
| Command, with the default policy | 1.1.x | 1.2.0 |
|---|---|---|
ls /tmp; rm -rf / |
ALLOW | REQUIRE_APPROVAL |
ls /tmp && sudo rm -rf / |
ALLOW | REQUIRE_APPROVAL |
echo x > /etc/passwd |
ALLOW | DENY |
cat README.md | head -5 |
ALLOW | DENY: no rule allows head |
git status && git diff |
ALLOW | ALLOW |
A pipeline is now allowed only when every command in it is, so add allow rules for the commands your agents chain. See Compound shell commands.
Policies load and match the way they read
- Rule blocks for the same scope are merged at load, with a warning. Two such blocks with different rate or cost limits are a load error.
- Likely-misspelled scope names warn (
scope "shel" … did you mean "shell"?), andagentguard validate --strictfails on any warning. url-only network and browser checks use the URL's host, as the policy reference always said.https://api.github.com@evil.com/is checked asevil.com.- The MCP gateway checks the URL's host. A
domainorhostargument next to aurlno longer stands in for it, so a model can't pair an allow-listed domain with a URL on another host. The Python MCP adapter is fixed the same way.
Secrets are masked in the audit trail
serve --audit-redact, on by default, masks keys, tokens, passwords, JWTs, private keys and credential headers before they reach the audit log, GET /v1/audit, the live stream, the dashboard and the pending-approvals list. It uses the same patterns as notifications, which gain sk-… API keys, Google keys, all GitHub token types, JWTs, PEM keys and Authorization headers, plus your policy's extra_patterns. The work happens in the audit workers, not on the /v1/check path. --audit-redact=false turns it off.
Audit and operations
agentguard auditshows the newest entries first (--order ascfor the old order), and so does the dashboard's history.GET /v1/auditgainsorder=asc|desc; its default is unchanged.- Decision counters survive archive pruning: the replay checkpoint is rewritten on every rotation and at shutdown.
serve --bind <host>keeps a keyed server on one address, such as127.0.0.1behind a reverse proxy on the same host. A non-loopback--bindwithout--api-keyrefuses to start.agentguard --versionworks like the other two binaries.
Default policy
find … -exec, -ok and -delete require approval. The filesystem MCP server's edit_file, move_file and create_directory tools are denied like write_file.
Python SDK
- The CrewAI adapter requires CrewAI 1.0+ and refuses older versions, on which it couldn't gate crew tool calls.
[all]now resolves to CrewAI 1.15, browser-use 0.11, LangChain 1.4 and MCP 1.28 with a cleanpip check. - The
mcpextra allows MCP 2.x. agentguard.__version__is new.
CI
The test_at_* suites (a real agent loop, the real LLM proxy, the MCP gateway against a real server) run in CI for the first time, and a new job installs [all] fresh and runs the whole Python suite.
Breaking changes
None in the public API. No field, route, flag, subcommand or schema version was removed or changed; everything new is additive.
The security fixes change behaviour:
- Shell chains, pipelines and redirections are checked command by command, so some that 1.1.x allowed are now denied or sent to approval.
- A second block for a scope now applies. Two blocks with different rate or cost limits make the policy fail to load.
url-only network and browser checks can be allowed where 1.1.x denied them, and MCP gateway calls with both aurland adomainargument are checked against the URL's host.- Audit content is masked by default, and
agentguard auditlists newest first. - The CrewAI adapter needs CrewAI 1.0+.
Upgrade notes
- Run
agentguard validate --strict --policy <your policy>with the 1.2.0 binary. - Replay recent shell ALLOWs from your audit log through
agentguard check --batchto see which chains 1.2.0 would stop, and add allow rules for the ones you want. - Swap the binaries and SDKs. Nothing on disk changes format, and rolling back to 1.1.x is safe, but it brings back the bypasses.
Details: MIGRATION.md § v1.1.x → v1.2.0.
Get started
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard@v1.2.0
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard-mcp-gateway@v1.2.0
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard-llm-proxy@v1.2.0
pip install --upgrade "agentguardproxy==1.2.0"
npm install @lictorate/agentguard@1.2.0
# Docker: no image is published; build one from the repository's Dockerfile
docker build -t agentguard:1.2.0 .Full change list: CHANGELOG.md § 1.2.0.
AgentGuard v1.1.1
AgentGuard 1.1.1 — Release Notes
Released: 2026-09-23
Headline: A patch release that corrects what the docs got wrong about names, addresses and examples, and closes one proxy bind check. The TypeScript SDK is on npm for the first time, as @lictorate/agentguard.
If you sent a security, conduct or support report to
cauaferraz@gmail.com, it was never received. That address doesn't exist; earlier docs misspelled it. Please resend to cauaferrazp@gmail.com.
Why this release exists
An audit checked every install command, contact address, example config and documented behaviour against what actually exists. Most of what it found was in the docs, and some of it pointed users at things that aren't ours or don't exist:
- The TypeScript install command named someone else's package. The docs said
npm install @agentguard/sdk. That npm package belongs to an unrelated project; the AgentGuard SDK had never been published to npm. - The security contact address didn't exist, so vulnerability reports sent to it went nowhere.
- Three release notes said to
docker pullan image that was never published. - The example MCP configs launched a fetch server that has never existed on npm, and the default policy denied every call to the fetch and GitHub examples even after approval.
- One code bug:
agentguard-llm-proxy --listen :PORTstarted on every network interface with no inbound auth.
What changed
LLM API Proxy: --listen :PORT needs --proxy-api-key
A listen address with no host binds every interface, but the proxy treated it as loopback and skipped its rule that non-loopback binds require --proxy-api-key. It now refuses to start (exit status 2) and suggests 127.0.0.1:PORT. If you ran the proxy this way on a reachable host, anyone who could reach the port could send requests through it to your configured provider, each one policy-checked and audited under the proxy's AgentGuard API key.
TypeScript SDK on npm as @lictorate/agentguard
npm install @lictorate/agentguard@1.1.1import { AgentGuard } from '@lictorate/agentguard';This first version was published by hand; from the next release, GitHub Actions publishes it through npm trusted publishing, with provenance.
Example MCP configs and the default policy
- The example configs for Claude Desktop, Cursor, Cline, Continue and Zed launch the official servers:
uvx mcp-server-fetchand GitHub'sghcr.io/github/github-mcp-serverimage. Each guide lists the launcher (npx, uvx or docker) each upstream needs. configs/default.yamlsends fetch and GitHub calls to human approval. Approved fetches still pass through the network allow-list; approved GitHub calls run.
Builds
go installbuilds show the update notice from the interactive subcommands, as documented, andagentguard versionprintsmodule vX.Y.Zor a git revision instead ofdev.servestill never makes the check;AGENTGUARD_NO_UPDATE_CHECK=1opts out.make docker-runpassesAGENTGUARD_API_KEY, which the container needs to listen on its published port, and stops with an explanation when the variable is unset.agentguard statussays when the server was started without--dashboardinstead of printing a JSON decode error.
Documentation
Corrected against the code and, where it mattered, against a running server: the MCP and LLM proxy quickstarts, the MCP Gateway and LLM API Proxy references, deployment (Compose now builds the image; the Kubernetes example gained the --policy flag it needed to start), the API reference (SSE event types, status codes, and the previously undocumented POST /v1/audit), both SDK references, the CLI reference, the adapter compatibility matrix, the Go and Node version floors, and SECURITY.md, which now supports the 1.1.x line. The hot-path latency rules CI enforces are now public in CONTRIBUTING.md.
Corrections to earlier releases
Earlier CHANGELOG entries, release notes and MIGRATION.md are corrected in place, each with a (Corrected in v1.1.1: …) note: the contact address; the TypeScript SDK was not on npm at 0.5.2, 0.9.0 or 1.0.0; no Docker image was ever published; v0.7.0 and v0.4.1 were never published; and links to a design document that was never committed.
Breaking changes
None in the public API: no exported Go identifier, config key, route, wire field, audit format or policy schema changed.
Two behaviour changes can affect an existing setup:
--listen :PORTon the LLM proxy now refuses without--proxy-api-key. This is a fail-closed correction.- The shipped
configs/default.yamlchanged for thefetchandgithubMCP namespaces. A copy you made earlier is unaffected.
Upgrade notes
- Binaries and Python SDK: a swap; nothing on disk changes.
- TypeScript SDK: install
@lictorate/agentguardand change imports from@agentguard/sdk. - LLM proxy: change
--listen :PORTto127.0.0.1:PORT, or add--proxy-api-key. - Rollback to 1.1.0 is safe, but brings back the
--listen :PORThole.
Details: MIGRATION.md § v1.1.0 → v1.1.1.
Get started
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard@v1.1.1
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard-mcp-gateway@v1.1.1
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard-llm-proxy@v1.1.1
pip install --upgrade "agentguardproxy==1.1.1"
npm install @lictorate/agentguard@1.1.1
# Docker: no image is published; build one from the repository's Dockerfile
docker build -t agentguard:1.1.1 .Full change list: CHANGELOG.md § 1.1.1.
AgentGuard v1.1.0
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.
lictorate's_AgentGuard_v1.0.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.
Semantics: COMPATIBILITY.md. Operator sizing: OPERATIONS.md. (Corrected in v1.1.1: this linked an internal design doc that was never published.)
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; the TypeScript SDK'spackage.json). (Corrected in v1.1.1: the TypeScript SDK was not published to npm at this release, and@agentguard/sdkis an unrelated package. The SDK is on npm as@lictorate/agentguardfrom 1.1.1.)
Breaking changes
One, made under the pre-1.0 reserved right: **approval resolutio...
AgentGuard_v0.9.0
AgentGuard 0.9.0 — Release Notes
Released: 2026-06-22
Headline: Surface-stabilization release — the step before a validated v1.0. AgentGuard's public surface is now stabilized: all three enforcement paths (MCP Gateway, LLM API Proxy, SDK / /v1/check) are verified end-to-end, the test suite is green, and v0.9 locks in the surfaces it intends to freeze at 1.0 while correcting one honesty defect. There are no new features and no breaking changes — the /v1/check wire protocol and the audit JSONL are byte-compatible with v0.7 (no schema bump).
The self-hosted Apache-2.0 build remains fully featured. The hosted, multi-tenant version (AgentGuard Cloud) lives at https://agentguard.lictorate.com.
Why this release exists
v0.9 is the last stop before a validated v1.0, and it exists to make the 1.0 promise credible rather than to add surface:
- The public surface needed to be written down before it can be frozen. v1.0's headline guarantee is additive-only evolution — no field, route, flag, or schema version removed or renamed without a major-version event. That promise is only meaningful if the surface it covers is enumerated. v0.9 enumerates it in a single contract document and ships nothing that would move it.
- One marketing claim was ahead of the code. The README and FAQ called the audit log "tamper-evident," but the code writes an append-only JSON-Lines log with no cryptographic integrity chain. Carrying an unbacked security claim into a validated 1.0 was not acceptable, so v0.9 trues it up.
- Two smaller accuracy gaps were worth closing on the way in — the
/v1/checklatency telemetry was measured before the audit enqueue (understating a slow audit backend), and the supported topology was never stated plainly. Both are now correct.
v0.9 ships only after additional testing and validation. It is a no-new-features, no-breaking-changes release by design.
What changed
The stabilization contract — docs/COMPATIBILITY.md
New document that declares the surfaces v0.9 stabilizes and intends to freeze at 1.0:
- the policy YAML schema (
version: "1"), - the
/v1/checkand/v1/t/{tenant}/...wire protocol, - the audit
schema_version: 2format, and - the CLI flags and subcommands.
It also states the additive-only intent that becomes a hard guarantee from 1.0: nothing in those surfaces is removed or renamed without a major-version event. Linked from the README docs table.
Audit "tamper-evident" claim corrected (the one real defect)
The README and FAQ marketed the audit log as "tamper-evident," but the code provides an append-only JSON-Lines log with no cryptographic integrity chain. The wording is now accurate: AgentGuard writes an append-only audit trail, and tamper-evidence is achieved by forwarding it to append-only / WORM storage (S3 Object Lock, a SIEM, or syslog) — AgentGuard does not seal the log itself. No code or on-disk format changed; this is a documentation correction only. (Closes internal-audit finding L4 by truth-up, not by crypto — in-process hash-chaining / Merkle checkpoints were evaluated and deferred out of core scope.)
/v1/check latency telemetry corrected
X-AgentGuard-Total-Ms and the agentguard_request_duration_ms SLO histogram are now measured end-to-end (after the audit enqueue), matching their documented "end-to-end" definition; previously they were captured before the audit write and understated a slow audit backend. Response-header / metric change only — no wire-format or gating change.
CI hot-path p99 latency gate
New TestEngineCheck_P99LatencyGate (pkg/policy) runs policy.Engine.Check with the persistence syncer active and fails if p99 crosses the 3 ms budget (measured baseline ≪0.1 ms for the bare engine; ~0.53 ms for the full HTTP /v1/check path). Wired as a dedicated step in .github/workflows/ci.yml alongside the existing HTTP-path persistence-on latency check, so a regression that lands blocking I/O on the hot path breaks the build.
Single-node posture stated plainly
README Limitations now declares single-node (replicas: 1) the supported, stable topology, with PostgreSQL / multi-node explicitly a v1.0 requirement (post-v0.9). No behavior change.
Version bump
Version set to 0.9.0 across all three binaries (agentguard, agentguard-mcp-gateway, agentguard-llm-proxy) and both SDKs (agentguardproxy on PyPI, @agentguard/sdk on npm).
Breaking changes
Nothing
The /v1/check request/response shapes, the audit schema_version: 2 JSONL format, the policy schema (version: "1"), and every CLI flag and subcommand are unchanged from v0.7. The Go-level surface is unchanged as well. Upgrading is a binary/SDK swap.
Upgrade notes
- No migration required. There is no file rewrite, no policy-schema change, no wire-protocol change, and no audit-format change. See
MIGRATION.md§ v0.7.0 → v0.9.0. - Go installers (
go install): pull the v0.9.0 tag (below). - Docker:
docker pullagainst the next published image after this release. - Python SDK:
pip install --upgrade "agentguardproxy==0.9.0". - TypeScript SDK:
npm install @agentguard/sdk@0.9.0. - (Optional) Forward your audit log to WORM storage if you want tamper-evidence — see the README audit bullet and
COMPATIBILITY.md.
Get started
# Install all three binaries at the v0.9.0 tag
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard@v0.9.0
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard-mcp-gateway@v0.9.0
go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard-llm-proxy@v0.9.0
# Or upgrade the SDKs only
pip install --upgrade "agentguardproxy==0.9.0"
npm install @agentguard/sdk@0.9.0Full change list: CHANGELOG.md § 0.9.0. Stabilized surfaces and the additive-only intent: docs/COMPATIBILITY.md.
AgentGuard_V0.6.0
AgentGuard v0.6.0 — Persistent Multi-Tenant State
v0.6 makes AgentGuard stateful and multi-tenant. The approval queue, rate-limit buckets, and cost accumulators now survive restarts, and a single instance can serve many tenants — each evaluated against its own policy with fully isolated state. It all stays off the hot path: measured /v1/check p99 is 0.53 ms with persistence enabled.
Highlights
🗄️ Durable, Zero-Config Persistence
agentguard serve now writes runtime state behind to a local SQLite database (agentguard.db, created automatically) and rehydrates it on boot. It's a write-behind, cold-path design — the store is never read or written on the /v1/check request path, so the <3 ms latency budget is untouched. A background syncer flushes snapshots on a ≥1 s ticker and on graceful shutdown. Use --persist=false for the previous pure-in-memory behavior.
🏢 Multi-Tenant Policies
Register per-tenant policies and serve them over the /v1/t/<tenant>/... routes. Each tenant is evaluated against its own policy, with isolated approvals, rate limits, cost accumulators, and audit. A tenant with no policy is denied (deny:tenant:not_found).
agentguard tenant put acme --policy acme-policy.yaml
agentguard tenant list
curl -X POST localhost:8080/v1/t/acme/check \
-d '{"scope":"shell","command":"deploy app"}' # evaluated against acme's policy
🔒 Tenant Isolation + An Audit Fix
Every per-request store is partitioned by tenant (rate-limit, cost, approvals, SSE). The audit schema gained a tenant_id field and /v1/t/<tenant>/audit is now scoped to its tenant — closing a cross-tenant audit-read leak. Single-tenant output is byte-identical (no schema_version bump).
🗃️ Optional SQLite Audit Backend
--audit-backend=store routes the audit trail into the store's indexed audit_entries table for a one-file deployment with indexed /v1/audit queries (always async-buffered to keep DB writes off the hot path). The default stays JSONL.
Technical Details
🛠️ New CLI Surface
- Serve flags:
--persist(defaulttrue),--store-dsn,--data-dir,--audit-backend=file|store - New commands:
agentguard tenant put,agentguard tenant list,agentguard tenant rm
🚀 Upgrade Notes
- Stateful by default: A first run creates
agentguard.db(+-wal/-shmsidecars) in the working directory. This is additive; the wire contract (schema_version: v1, the/v1/t/{tenant}/...routes) is unchanged. Run with--persist=falseto keep the old behavior. - Single-node persistence: State is not yet shared across instances. Keep replicas to
1; a PostgreSQL backend for multi-node is next.
🐛 Also Fixed
- File-watcher transient-read race: Fixed an atomic-replace edit issue (Windows
ERROR_SHARING_VIOLATION) — reloads now self-heal instead of silently dropping a policy change. - New dependency: Added
modernc.org/sqlite(pure-Go, no CGO).
Note: Full details are available in
CHANGELOG.md.
v0.5.2 — MCP gateway fix + toolchain refresh + supply-chain monitoring
AgentGuard 0.5.2 — Release Notes
Released: 2026-06-02
Headline: Maintenance release. Toolchain refresh (Go 1.22→1.25, Alpine 3.19→3.22, TS 5→6, Jest 29→30, Node floor 18→20 — every previous pin was past upstream EOL), supply-chain monitoring added (govulncheck on every push, SBOM SPDX + CycloneDX attached to every release), and a real bug fix in the MCP Gateway that restores the README's headline quickstart path against the current published @modelcontextprotocol/server-filesystem.
The self-hosted Apache-2.0 build remains fully featured. The hosted, multi-tenant version (AgentGuard Cloud) lives at https://agentguard.lictorate.com.
Why this release exists
Three independent reasons converged into one patch release:
- MCP Gateway quickstart was broken on the current upstream filesystem-server. The README's first integration path tells users to spin up
npx -y @modelcontextprotocol/server-filesystem /tmpbehindagentguard-mcp-gateway. The current published version of that filesystem-server enforces the MCP spec strictly:params.capabilitiesis required oninitialize. The gateway shipped through v0.5.1 declared the fieldomitemptyin its Go struct and normalised nothing at the send site, so when a host didn't pass capabilities the gateway forwarded the request with the field missing — and the upstream rejected it withexpected object, received undefined. Anyone following the README quickstart in the last several weeks hit this. v0.5.2 fixes it. - The full toolchain was overdue. Go 1.22 went EOL when 1.24 landed (only 1.25/1.26 are upstream-supported now). Alpine 3.19 was EOL before April 2026. Node 18 reached upstream EOL 2025-04-30. golangci-lint v1 is legacy; v2 is the only line getting fixes. None of these had to break v0.5.1 today, but they were all carrying credibility risk for the v1.0 push.
- Supply-chain hygiene is table stakes for the commercial build. The hosted AgentGuard Cloud effort needs to answer enterprise procurement: do you scan dependencies for CVEs, do you produce SBOMs, do you pin by hash. v0.5.2 adds the first two as automated workflows; the third is already provided by Go modules +
go.sum.
A second motivator: a maintainer access dispute around fsnotify surfaced in May 2026. v1.10.1 itself is legitimate (signed-off checksum pinned in go.sum), but the situation underscored that detection layering — dependabot + govulncheck + SBOM — is the right response to a transitive-dep supply-chain risk, not pre-emptively yanking the package.
Headline fix
MCP Gateway initialize is now spec-compliant
pkg/mcpgw/protocol.go and pkg/mcpgw/transport.go. The InitializeParams struct's Capabilities field was tagged json:"capabilities,omitempty", which meant a nil map silently dropped the field. The MCP spec requires capabilities to be present and an object. Strict upstreams (current @modelcontextprotocol/server-filesystem and any other spec-conformant strict server) reject the missing-field initialize with a validation error visible only in the upstream subprocess's stderr — opaque to anyone driving the gateway through a host like Claude Desktop or Cursor.
The fix is two surgical edits:
- The struct tag no longer has
omitempty— the field is always emitted. - The send site (
StdioUpstream.Initialize) normalises nil →map[string]interface{}{}so it serialises as{}instead ofnull(strict servers also rejectnull).
Verified end-to-end against the real npx -y @modelcontextprotocol/server-filesystem upstream: handshake, tools/list (returns the filesystem-server's 14-tool inventory with the gateway's fs: namespace prefix), and tools/call (policy-gated against the central server, denied per default policy with proper synthetic-refusal back to the client).
Other fixes
Policy watcher modTime race
pkg/policy/watcher.go. reload() updated w.modTime before attempting to parse. When a parse failed, the modTime was still advanced — effectively consuming a future mtime. A subsequent good save whose mtime happened to fall before the advanced value was silently skipped. The fsnotify v1.10.1 bump delivers events faster, which exposed this race reliably in TestWatchFile_BadYAMLKeepsWatcherAlive on Linux CI. modTime now advances only on successful parse.
test_guardedtool_batch_all_allowed order assertion
plugins/python/tests/test_langchain.py. The test asserted calls == ["a","b","c"] — execution order of a concurrent batch(). It passed by luck on Python 3.10–3.12's async scheduler and failed on 3.13. The output list out is still asserted exactly (LangChain preserves input/output alignment); only the calls invocation-order list is now sorted before comparison.
Toolchain & dependency refresh
| Was | Now | Why | |
|---|---|---|---|
| Go toolchain | 1.22 | 1.25 | 1.22 EOL when 1.24 landed; 1.25/1.26 are the only upstream-supported lines |
| Docker runtime base | alpine:3.19 |
alpine:3.22 |
3.19 EOL'd well before April 2026; 3.22 is supported through 2027-05 |
Node floor (engines.node) |
>=18.0.0 |
>=20.0.0 |
Node 18 EOL 2025-04-30 |
| TS SDK CI matrix | [18,20,22] |
[20,22,24] |
Drop EOL'd 18; add 24 (current Active LTS) |
| TypeScript | ^5.3.0 |
^6.0.0 |
Required "types": ["node", "jest"] in tsconfig (TS 6 stopped auto-discovering @types) |
| Jest | ^29.7.0 |
^30.0.0 |
Required dropping the two-generic jest.fn<Ret,Args>(…) signature at 9 call sites |
ts-jest |
^29.1.0 |
^29.4.0 |
No v30 exists; 29.4.x supports Jest 30 via peer deps |
@types/jest |
^29.5.0 |
^30.0.0 |
matches the Jest major |
@types/node |
^20.0.0 |
^22.0.0 |
matches a supported Node LTS |
| Python CI matrix | [3.10,3.11,3.12] |
[3.10,3.11,3.12,3.13] |
Trove classifier already claimed 3.13; CI now exercises it |
| golangci-lint | action@v6, v1.61.0 | action@v8, v2.12.2 | v1 line is legacy; v2 is the only one getting fixes |
github.com/fsnotify/fsnotify |
v1.9.0 | v1.10.1 | Refresh via go get -u && go mod tidy |
golang.org/x/sys (indirect) |
v0.13.0 | v0.45.0 | Same |
gopkg.in/yaml.v3 stays at v3.0.1 (canonical latest).
Supply-chain monitoring (new)
vulncheck CI job
Blocking. govulncheck runs on every push and pull request. Reachability-aware: only fires when a known-vulnerable symbol is actually called from project code, not when it merely appears in go.sum. This keeps the gate quiet on theoretical issues in unused transitive code while still catching the cases that matter. Defined in .github/workflows/ci.yml.
SBOM on every release
New workflow at .github/workflows/release-sbom.yml. Triggers on release: published. Builds the agentguard binary with the same flags as the Dockerfile, runs syft against the compiled artefact (not the source tree — scanning the binary records exactly what shipped, including the Go toolchain version), and attaches both formats:
agentguard-vX.Y.Z.spdx.json— SPDX is the ISO/IEC 5962 standard SOC 2 / FedRAMP auditors ask for by name.agentguard-vX.Y.Z.cdx.json— CycloneDX is what most security scanners (Dependency-Track, Grype, Snyk) ingest natively.
Producing both costs the workflow nothing extra and avoids "can you regenerate in X format" requests. v0.5.2 is the first release to exercise this workflow — confirm both assets are attached to the GitHub release before relying on it for v0.6.
.golangci.yml
First project-level golangci-lint config. Documents which patterns are intentionally excluded and why — so the next linter major bump (eventually v3) doesn't require re-discovering the rationale. errcheck off on _test.go files entirely; in production code, off only for conventional cleanup-path Close/Fprint receivers (stdlib io.Closer, *os.File, *sql.DB, *sql.Rows, *fsnotify.Watcher, and the project's audit Logger, policy.PolicyProvider, policy.Engine).
Breaking changes
TypeScript SDK consumers
If you embed the @agentguard/sdk source (rather than depending on the npm package), the floor moves to Node ≥ 20 and the build now requires TypeScript 6. Standard caret-pinned consumers ("@agentguard/sdk": "^0.5.2") are unaffected — the runtime behaviour and Guard API surface are identical.
MCP Gateway hosts on very old MCP clients
Almost certainly a non-event, but: if a client was relying on the gateway forwarding an initialize without capabilities to a permissive upstream, that request now always carries capabilities: {}. Any conforming upstream behaves identically; non-conforming upstreams that crashed on empty-object capabilities (we have never seen one) would now surface.
Nothing else
The Guard API, audit log schema, policy YAML schema, HTTP wire protocol, and CLI flag surface are all unchanged.
Upgrade notes
- Go installers (
go install): nothing to do;go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard@latestpulls v0.5.2. - Docker:
docker pullagainst the next published image after this release. - Python SDK:
pip install --upgrade agentguardproxy. - TypeScript SDK:
npm install @agentguard/sdk@^0.5.2. If you were locked at^5.3.0for TypeScript in your project for any reason, the SDK now builds against TS 6 — but for consumers of the compiled package (the normal case), no action. - MCP Gateway operators on v0.5.1: the README's quickstart will now succeed against
npx -y @modelcontextprotocol/server-filesystem. No config change required; you may delete any--upstreamworkarounds that pinned an older filesystem-server.
Known issues
pkg/policywatcher tests have residual timing flake on Windows under heavy parallelgo test ./...load.os.Renamesemantics on Windows produce file-lock races that don't reflect Linux production behaviour. CI is Linux-only and unaffected; loc...