Skip to content

Vigils v0.2.0-beta.1

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Jun 10:39
· 13 commits to main since this release

First public beta. Vigil grows from "MCP gateway only" into a local data-flow control
plane
: vigil-hub hook extends secret protection to an agent CLI's native tool calls
(Bash / Edit / …), covering Claude Code + Codex + Gemini + Cursor — not just MCP servers.
We're shipping it as a beta to gather real-world feedback: run vigil-hub setup, try the
postures, and tell us anything surprising. Bug reports welcome.

⚠️ Behavior change (BREAKING for defaults)

  • Default install surface is now the hook. vigil-hub setup (no flags) registers the
    agent-CLI hook by default (Claude as the primary surface, plus any detected Codex / Gemini /
    Cursor) instead of MCP wrapping. MCP wrap is demoted to the explicit setup --mcp (its
    code and behavior are fully preserved — use it when you only want to protect an MCP tool
    flow). setup --all still does both in one step.
  • Default posture is Low. A secret:// placeholder reaching a native tool is allowed
    at Low (α1 used to always deny). Three tiers: Low (deny only the highest risk — bare
    hard-fingerprint secrets — plus a reserved ledger-tamper tier whose detection isn't wired
    yet) / Medium (+ placeholder ask) / High
    (= the old enforce, deny everything). A bare real credential is denied in every tier (a
    non-negotiable floor). Switch with vigil-hub posture set|show.
  • A hook ask is now co-approval. At Medium, a placeholder's ask enters Vigil's approval
    queue with a bounded wait; both Vigil (desktop / CLI) and the tool chain's own UI can
    approve — first approver wins (atomic state-machine arbitration), and it falls back to the
    tool-chain prompt on timeout. The MCP-wrap approval-queue behavior is unchanged.

Added

  • Multi-agent hook adapter (hook.rs): a normalization layer that maps event and field
    names across Claude / Codex / Gemini / Cursor, then routes the response per CLI (Claude
    deny = exit 2 + stderr; Codex / Gemini / Cursor = exit 0 + each one's JSON contract). A
    bare secret is denied on any tool (including mcp__*) — the single defense-in-depth line.
  • Multi-agent hook registration (setup_hooks.rs): Codex ($CODEX_HOME/hooks.json),
    Gemini (~/.gemini/settings.json), and Cursor surfaces, each idempotent, with --uninstall
    removing only Vigil's own entries. If Codex config.toml has [features] hooks = false,
    setup warns and never rewrites it. The Claude surface is completed (PreToolUse +
    PostToolUse + timeout).
  • vigil-hub posture show|set <low|medium|high>: a turnkey entry to the three tiers
    (atomic config write + an audit event for every change).
  • Execution-boundary injection (α2): on PreToolUse, a secret://<alias> placeholder inside
    a boundary tool (Bash / shell) is resolved to its real value via a lease and rewritten
    inline into updatedInput for the host to execute — the model transcript only ever
    sees the placeholder
    . Claude only (the CLI proven to honor updatedInput). Real values
    never reach audit / stderr / notes (sha256 fingerprints only).
  • PostToolUse result re-redaction: before a boundary tool's result returns to the LLM, the
    real values of declared secrets are reverse-substituted back to secret://<alias> (plus a
    hard-fingerprint scrub as defense-in-depth), via Claude's updatedToolOutput. A declared
    secret that can't be resolved, or any residue found on self-check, triggers a fail-closed
    truncation
    .

Security invariants

  • Fail-closed by construction: the hook never returns an error or panics; a parse failure,
    an injection failure, a re-redaction failure, or a missing ledger all converge to
    deny-or-truncate (deny is exit 2 — exit 1 is fail-open and is never used to block).
  • Zero plaintext: a real value is exposed at a single point and flows straight to its
    injection target / re-redaction substitution; audit, reasons, notes, and stderr only ever
    carry the alias name + a sha256. Byte-level E2E confirms real values never hit disk.

Known scope limitations (this beta)

  • Re-redaction covers only a boundary tool's direct result; it does not track a secret's
    second-order propagation (a boundary command writes to disk → a non-boundary tool reads
    it back). Full coverage needs egress-side (model-API proxy) interception.
  • inject / re-redact use the OS keyring as the value backend, but keyring population has no
    turnkey CLI entry yet
    (the next increment); injection currently requires registering the
    hook command with --inject --secrets by hand.
  • A full real-machine dual-CLI (Claude Code + Codex live) inject / re-redact round-trip is
    still pending a controlled environment; the binary layer and unit tests already cover every
    decision and protocol shape.

Also in this release — bug fixes

  • DEF-004: the firewall's project boundary now actually binds — --project-root flag,
    defaulting to the gateway's working directory.
    Found in real-machine testing.
    • The bug: every production entrypoint (serve / wrap / demo / desktop embed) started
      the firewall with an empty set of project roots, and the policy engine's Outside
      condition is vacuously true on an empty set — so the built-in deny-outside-project rule
      (priority 150) treated the entire filesystem as "outside the project", while its
      counterpart approve-repo-write (priority 80) could never match. The Inside/Outside
      boundary semantics were inverted wholesale: any call recognized as a filesystem write was
      hard-denied in every posture (monitor only downgrades the default-deny floor, not
      explicit Deny rules), with an audit reason that falsely claimed "writes OUTSIDE project".
      It went unnoticed for so long because most wrapped third-party tool names aren't in the
      effect-extraction vocabulary — no FsWrite extracted, rule never fired, calls fell to the
      floor and were observe-allowed under monitor.
    • Fail-safe guard in the policy engine: with empty roots, Outside no longer asserts
      "outside the project" (it doesn't match), so writes fall to the default-deny floor —
      still fail-closed, and the audit reason is now the honest "no rule matched" instead of a
      fabricated boundary violation. The risk scorer follows the same semantics (no more +30
      "outside-project write" score on empty roots), and its root matching is now
      case-insensitive on Windows, aligned with the policy engine.
    • serve / wrap accept a repeatable --project-root <DIR>; omitted, the boundary
      defaults to the process working directory (agents launch the gateway inside the project,
      matching git/cargo directory semantics). Roots are normalized to the same POSIX form the
      path extractor emits (canonicalized, \/, \\?\ prefix stripped) — without this,
      prefix comparison on Windows silently never matches and the boundary is inert.
    • Visible change under enforce: writes inside the boundary now route to the
      approve-repo-write approval queue (previously hard-denied); writes outside are still
      blocked by deny-outside-project, with the reason pointing at a real boundary violation.
    • The startup banner prints the bound boundary (project boundary -> <roots> / NONE),
      so a gateway spawned from the wrong directory is visible at a glance.
    • SDK FirewallBuilder::project_roots normalizes roots in build() the same way, so
      native-form paths (C:\proj) from consumers compare correctly.
    • demo / desktop embed intentionally keep empty roots (self-contained simulation / no
      meaningful CWD for a GUI); the engine guard covers them. Adversarially reviewed.

中文

首个公开测试版。 Vigil 从"仅 MCP 网关"成长为本地数据流控制平面:vigil-hub hook
把 secret 防护扩到 agent CLI 的原生工具调用(Bash / Edit / …),覆盖
Claude Code + Codex + Gemini + Cursor —— 不再局限于 MCP server。我们以 beta 形式发布以收集
真实反馈:跑 vigil-hub setup、试试三档姿态,把任何意外告诉我们。欢迎提 bug。

⚠️ 行为变更(影响默认行为)

  • 默认安装面现在是 hook。 vigil-hub setup(无 flag)默认注册 agent CLI hook(Claude 为
    主面,外加检测到的 Codex / Gemini / Cursor),不再默认 MCP wrap。MCP wrap 降级为显式
    setup --mcp
    (代码与行为完全保留 —— 只想保护 MCP 工具流时用它)。setup --all 仍一步两者全做。
  • 默认姿态为 Low。 到达原生工具的 secret:// 占位符在 Low 档放行(α1 时是恒 deny)。
    三档:Low(仅拦最高风险 —— 裸硬指纹 secret;账本篡改档位已在决策表预留但检测尚未接线)/
    Medium(+ 占位符 ask)/
    High(= 旧 enforce,全量 deny)。裸真凭据在任何档位恒 deny(不可降级的硬底线)。
    vigil-hub posture set|show 切换。
  • hook 的 ask 现在是共同批准。 Medium 档下,占位符的 ask 进入 Vigil 审批队列有界等待;
    Vigil(desktop / CLI)与工具链自身 UI 两边都能批准 —— 先批者生效(审批状态机原子仲裁),
    超时回退工具链提示。MCP wrap 的审批队列行为不变。

新增

  • 多 agent hook adapter(hook.rs):归一层,把事件名与字段名跨 Claude / Codex / Gemini /
    Cursor 归一,再按 CLI 分流响应(Claude deny = exit 2 + stderr;Codex / Gemini / Cursor =
    exit 0 + 各自 JSON 契约)。裸 secret 在任何工具(含 mcp__*)恒 deny —— 唯一的纵深防御线。
  • 多 agent hook 注册(setup_hooks.rs):Codex($CODEX_HOME/hooks.json)、Gemini
    (~/.gemini/settings.json)、Cursor 各面,均幂等,--uninstall 仅删 Vigil 自有 entry。若
    Codex config.toml[features] hooks = false,setup 仅警告、绝不改写。Claude 面完整化
    (PreToolUse + PostToolUse + timeout)。
  • vigil-hub posture show|set <low|medium|high>:三档姿态的 turnkey 入口(原子写配置 +
    每次变更一条审计事件)。
  • 执行边界注入(α2):PreToolUse 时,边界工具(Bash / shell)内的 secret://<alias> 占位符
    经 lease 授权解析为真值,内联重写updatedInput 交宿主执行 —— 模型 transcript 始终只见
    占位符
    。仅 Claude(实证支持 updatedInput)。真值绝不进审计 / stderr / note(仅 sha256 指纹)。
  • PostToolUse 结果再脱敏:边界工具结果回 LLM 前,声明 secret 的真值经逆向替换
    secret://<alias>(+ 硬指纹 scrub 作纵深防御),经 Claude updatedToolOutput 改写。声明的
    secret 无法解析、或自检发现残留 → fail-closed 裁剪

安全不变量

  • fail-closed by construction:hook 永不返错或 panic;解析失败、注入失败、再脱敏失败、缺
    ledger 一律收敛为 deny 或裁剪(deny 走 exit 2 —— exit 1 是 fail-open,绝不用作拦截)。
  • 零明文:真值仅在单点暴露,直达注入目的地 / 再脱敏替换;审计、reason、note、stderr 全程
    只含 alias 名 + sha256。字节级 E2E 验证真值不落盘。

已知范围边界(本测试版)

  • 再脱敏覆盖边界工具的直接结果;不追踪 secret 的二次传播(边界命令落盘 → 非边界
    工具读出)。完整覆盖需 egress 侧(模型 API 代理)拦截。
  • inject / re-redact 走 OS keyring 真值后端,但 keyring 填充尚无 turnkey CLI 入口(下一增量);
    注入当前需手动用 --inject --secrets 注册 hook 命令。
  • 完整真机双 CLI(Claude Code + Codex 实跑)inject / re-redact 往返 E2E 待受控环境验证;
    二进制层与单测已覆盖全部决策与协议形状。

本版同时包含 —— bug 修复

  • DEF-004:firewall 项目边界从未真正生效 —— 新增 --project-root flag,缺省为网关工作目录。
    真机测试中发现。
    • bug 本体:所有生产入口(serve / wrap / demo / 桌面 embed)启动 firewall 时项目根
      集合为,而 policy 引擎的 Outside 条件对空集合恒判 true —— 内置规则
      deny-outside-project(priority 150)把整个文件系统判成"项目外",对称的
      approve-repo-write(priority 80)永不匹配,Inside/Outside 边界语义整体反转:凡被识别为
      文件写的调用在所有姿态被硬 deny(monitor 只降级 default-deny floor,不降级显式
      Deny 规则),且审计 reason 谎报"writes OUTSIDE project"。长期未暴露是因为多数被 wrap 的
      第三方工具名不在 effect 提取词表内 —— 提取不出 FsWrite,规则根本不触发,调用落 floor
      被 monitor 观察放行。
    • policy 引擎 fail-safe 守门:空 roots 时 Outside 不再断言"项目外"(返不匹配),写操作
      落 default-deny floor —— 仍 fail-closed,且 reason 诚实为 "no rule matched" 而非伪造的
      越界。风险评分器同语义(空 roots 不再 +30 "越界写"评分),其根匹配在 Windows 下补齐
      大小写不敏感,与 policy 引擎对齐。
    • serve / wrap 新增可重复的 --project-root <DIR>;省略时缺省 = 进程工作目录
      (agent 在项目目录里启动网关,与 git/cargo 的目录语义一致)。根按路径提取器同款 POSIX
      形式归一(canonicalize、\/、剥 \\?\ 前缀)—— 否则 Windows 下前缀比较静默不
      匹配,边界形同虚设。
    • enforce 姿态下的可见变更:边界的写操作现在走 approve-repo-write 审批通道
      (此前被硬 deny);边界的写仍被 deny-outside-project 拦截,reason 如实指向真实
      越界路径。
    • 启动 banner 打印绑定的边界根(project boundary -> <roots> / NONE),从错误目录
      spawn 的网关一眼可见。
    • SDK FirewallBuilder::project_rootsbuild() 时同样归一,消费者传 C:\proj 原生
      形式也能正确前缀比较。
    • demo / 桌面 embed 有意保持空 roots(自包含模拟 / GUI 无工作目录语义),由引擎守门
      兜底。已通过对抗式审查。

Downloads — which file do I want?

  • Desktop app (most users): the installer for your OS — Windows Vigils_*_x64-setup.exe (or .msi), macOS Vigils_*.dmg, Linux .AppImage / .deb / .rpm. Gives you the GUI: Activity Feed, Approval Queue, Server Registry.
  • CLI gateway (put Vigils in front of an AI agent — Claude Code / Codex / Cursor / Zed): vigils-cli-<platform> (contains vigil-hub + vigil-native-host). This is the MCP proxy your agent connects to.
  • Browser extension (guard pasting/typing secrets into AI web apps, Chrome MV3): vigils-chrome-extension.zip — unzip, then load unpacked at chrome://extensions.
  • The .sig and Vigils.app.tar.gz files are desktop auto-updater artifacts — you do not need to download them.

New here? Full setup & agent-integration guide: https://duncatzat.github.io/vigils

Early releases are unsigned; your OS may show a Gatekeeper / SmartScreen prompt on first run.

Apache-2.0 · https://vigils.ai · Full changelog