Skip to content

Releases: lictorate/vanzik

Vanzik v2.0.0

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 26 Oct 21:17
3cae56d

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. See MIGRATION.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.db to vanzik.db and updates setup.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-backup of 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.0

Releases 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

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 16 Oct 01:17
57c6392

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_prior deny 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, eval and wrappers such as timeout or env, so some commands that used to pass now ask for approval.
  • GET /v1/audit now answers 400 to a since or until that isn't an RFC 3339 time, where 1.3 ignored both parameters. See MIGRATION.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:

  1. "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.
  2. "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 verify names the exact place.
  3. "How do I know my policy does what I think?" agentguard test runs it against test cases in CI, including the raw tool calls your coding agents send.
  4. "What did this agent and its subagents actually do?" Sessions tie the calls together, and the dashboard replays them step by step.
  5. "Can we hand this to an auditor?" agentguard report writes 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 rule deny: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 verify checks 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 --anchor and even someone holding the key can't rewrite history before it.

  • New installs are sealed by default. agentguard setup generates 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_APPROVAL

agentguard 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-Session header;
    • the hooks, which also record which subagent made each call.
  • Session API. GET /v1/sessions lists 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/audit and agentguard audit gain since, 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.html

The 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-18 is 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 '…' and env 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 approval rm -rf and sudo need. This has affected Claude Code since 1.3.
    • AgentGuard now checks the commands inside:
      • sh/bash/zsh and other shells' -c scripts;
      • eval, and here-s...
Read more

AgentGuard v1.3.0

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 27 Sep 00:13
6be11ab

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 --upstream command across several args entries, put it back in one string: 1.2.0 started that MCP server without its arguments. See MIGRATION.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:

  1. 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.
  2. Set it up from a menu. agentguard setup does the rest and can undo it.
  3. Guard the agent people actually use. Claude Code's tool calls go through your policy.
  4. 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 | iex

The 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 server is the command's name; serve still works.
  • Flags work before or after arguments. agentguard approve ap_1 --url X used to approve on localhost.
  • agentguard validate policy.yaml validates 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_POLICY or the installer's folder when --policy isn't given; AGENTGUARD_URL works 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-file keeps the key out of command lines and service definitions.
  • The update notice names the command that updates (agentguard setup, docker pull or go 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_URL and the key agentguard setup saves are used when no flag gives a server or key.

Upgrade notes

  1. Check scripts and MCP client configs for the command-line changes above.
  2. Rerun the one-line installer, or go install github.com/Caua-ferraz/AgentGuard/cmd/...@v1.3.0. Nothing on disk changes format.
  3. Run agentguard setup if 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.0

Full change list: CHANGELOG.md § 1.3.0.

AgentGuard v1.2.0

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 24 Sep 23:12
b404788

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 * or git *, 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 with agentguard validate --strict and the replay recipe in MIGRATION.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:

  1. 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 ~) and echo x > /etc/passwd were all allowed, getting past its sudo and rm -rf approval rules.
  2. A second rule block for a scope was ignored whenever the first one decided, so a separate deny block could silently do nothing, and validate said the policy was valid.
  3. Secrets were stored verbatim in the audit trail and shown by the audit API, the live stream and the dashboard.
  4. pip install agentguardproxy[all] installed CrewAI 0.193, on which a crew ran its tools without a single policy check.
  5. Rerunning the test against the fixes found one more bypass: the MCP gateway checked a model-supplied domain argument instead of the host of the URL the tool would fetch.
  6. 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 audit showed 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"?), and agentguard validate --strict fails 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 as evil.com.
  • The MCP gateway checks the URL's host. A domain or host argument next to a url no 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 audit shows the newest entries first (--order asc for the old order), and so does the dashboard's history. GET /v1/audit gains order=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 as 127.0.0.1 behind a reverse proxy on the same host. A non-loopback --bind without --api-key refuses to start.
  • agentguard --version works 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 clean pip check.
  • The mcp extra 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 a url and a domain argument are checked against the URL's host.
  • Audit content is masked by default, and agentguard audit lists newest first.
  • The CrewAI adapter needs CrewAI 1.0+.

Upgrade notes

  1. Run agentguard validate --strict --policy <your policy> with the 1.2.0 binary.
  2. Replay recent shell ALLOWs from your audit log through agentguard check --batch to see which chains 1.2.0 would stop, and add allow rules for the ones you want.
  3. 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

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 24 Sep 15:37
a72e1a4

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:

  1. 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.
  2. The security contact address didn't exist, so vulnerability reports sent to it went nowhere.
  3. Three release notes said to docker pull an image that was never published.
  4. 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.
  5. One code bug: agentguard-llm-proxy --listen :PORT started 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.1
import { 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-fetch and GitHub's ghcr.io/github/github-mcp-server image. Each guide lists the launcher (npx, uvx or docker) each upstream needs.
  • configs/default.yaml sends fetch and GitHub calls to human approval. Approved fetches still pass through the network allow-list; approved GitHub calls run.

Builds

  • go install builds show the update notice from the interactive subcommands, as documented, and agentguard version prints module vX.Y.Z or a git revision instead of dev. serve still never makes the check; AGENTGUARD_NO_UPDATE_CHECK=1 opts out.
  • make docker-run passes AGENTGUARD_API_KEY, which the container needs to listen on its published port, and stops with an explanation when the variable is unset.
  • agentguard status says when the server was started without --dashboard instead 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 :PORT on the LLM proxy now refuses without --proxy-api-key. This is a fail-closed correction.
  • The shipped configs/default.yaml changed for the fetch and github MCP namespaces. A copy you made earlier is unaffected.

Upgrade notes

  • Binaries and Python SDK: a swap; nothing on disk changes.
  • TypeScript SDK: install @lictorate/agentguard and change imports from @agentguard/sdk.
  • LLM proxy: change --listen :PORT to 127.0.0.1:PORT, or add --proxy-api-key.
  • Rollback to 1.1.0 is safe, but brings back the --listen :PORT hole.

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

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 14 Sep 00:21

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

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 22 Jul 14:13

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 run replicas: 1 today, 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:

  1. 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.
  2. 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.
  3. 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 (default 2s) — cadence of the background reconciliation loop. Takes effect only with a Postgres --store-dsn; on SQLite it is forced to 0 and the loop never starts.
  • --approval-validity (default 5m) — how long a resolved ALLOW is honored by the /v1/check approval-id retry, measured from resolution. 0 restores the unbounded pre-1.0 window. The default matches the SDKs' wait_for_approval poll 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 Allow path stays 0-alloc.
  • F3 — globMatch documentation corrected + non-fatal policy-lint warning. A path pattern with / but no ** (e.g. /workspace/*) matches across / (so /workspace/a/b/secret.env matches). 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-action inference now matches the Go transports. The adapters inferred the filesystem action by substring with a narrower verb set, so several verbs sent no action (and set_target mis-fired on "get"). Inference is now the same prefix-matched verb groups as gateclient.InferFilesystemAction, pinned by a parity test. Operators whose policies key on filesystem action may see new (correct) matches through the Python adapters.
  • Audit flush workers now isolate panics from the underlying logger. A panic inside underlying.Log previously 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.com did not match the DNS-equivalent request EVIL.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 govulncheck job and the cross-ecosystem dep-audit gate 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 both govulncheck and the live OSV.dev dep-audit gate.
  • 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 CI govulncheck gate).

Documentation & metadata

  • MCP Gateway protocol-coverage docs truthed up (no behavior change). The gateway brokers tools/* only — resources/* and prompts/* are masked (MethodNotFound), not "forwarded verbatim" as the old docs claimed; notifications/cancelled is documented as a latent no-op. Dropped server-initiated frames carrying a method now 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 (agentguardproxy on PyPI; the TypeScript SDK's package.json). (Corrected in v1.1.1: the TypeScript SDK was not published to npm at this release, and @agentguard/sdk is an unrelated package. The SDK is on npm as @lictorate/agentguard from 1.1.1.)

Breaking changes

One, made under the pre-1.0 reserved right: **approval resolutio...

Read more

AgentGuard_v0.9.0

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 09 Jul 04:03

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:

  1. 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.
  2. 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.
  3. Two smaller accuracy gaps were worth closing on the way in — the /v1/check latency 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/check and /v1/t/{tenant}/... wire protocol,
  • the audit schema_version: 2 format, 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 pull against 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.0

Full change list: CHANGELOG.md § 0.9.0. Stabilized surfaces and the additive-only intent: docs/COMPATIBILITY.md.

AgentGuard_V0.6.0

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 08 Jun 22:37
6566dce

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 (default true), --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/-shm sidecars) in the working directory. This is additive; the wire contract (schema_version: v1, the /v1/t/{tenant}/... routes) is unchanged. Run with --persist=false to 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

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 02 Jun 00:23

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:

  1. 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 /tmp behind agentguard-mcp-gateway. The current published version of that filesystem-server enforces the MCP spec strictly: params.capabilities is required on initialize. The gateway shipped through v0.5.1 declared the field omitempty in 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 with expected object, received undefined. Anyone following the README quickstart in the last several weeks hit this. v0.5.2 fixes it.
  2. 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.
  3. 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 of null (strict servers also reject null).

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@latest pulls v0.5.2.
  • Docker: docker pull against 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.0 for 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 --upstream workarounds that pinned an older filesystem-server.

Known issues

  • pkg/policy watcher tests have residual timing flake on Windows under heavy parallel go test ./... load. os.Rename semantics on Windows produce file-lock races that don't reflect Linux production behaviour. CI is Linux-only and unaffected; loc...
Read more