Introduction
Vigils is a local-first control plane for AI agents:
- Action firewall (
Firewall::evaluate) — fail-closed effect gating - Audit ledger (SHA-256 hash chain) — tamper-evident decision history
- Privacy filter — hard-fingerprint rules (always on) + optional ONNX PII/injection model (opt-in via
--engine ml|auto; not in default release builds — see Privacy Filter) - MCP hub (Model Context Protocol server registry + descriptor pinning)
- Approval queue (human-in-the-loop for risky effects)
- Sandbox runner (Wasm + native, Linux Landlock LSM)
Vigils sits between AI agents and the effectful tools / APIs they touch, gating each action through redaction + policy + audit + approval — and everything stays on your machine.
Two ways in
- Desktop app — a few clicks. Open the Aegis command deck and click Deploy Guardian: Vigils detects every installed agent, backs up its config, wires hook + MCP-gateway protection into all of them and shows per-agent status. Missing engine? One click downloads it (signed manifest + SHA-pin, fail-closed). Close the window — it keeps guarding from the tray. See the Desktop Quickstart.
- CLI — one command.
vigil-hub setup --alldoes the same from a terminal, andvigil-hub demoshows the whole protection story in 30 seconds with zero setup. See Agent Integration.
What it protects against — and what it doesn’t
Vigils is defense in depth, not an airtight barrier. Being honest about the boundary lets you rely on it correctly.
Reliably catches
- Plaintext credential leaks — 17 hard-fingerprint classes (AWS / Alibaba Cloud / Tencent Cloud keys, GitHub / GitLab / Hugging Face tokens, Google API keys, Slack webhooks and tokens, Stripe keys, private-key PEM blocks, credential-bearing DB URLs, …) appearing verbatim in tool calls, browser pastes, or tool results.
- Reversible redaction round-trip — the model / logs / audit see only
secret://<alias>placeholders; the real value is injected only at the local execution boundary. - Tamper-evident audit (SHA-256 chain, falsifiable via
vigil-hub verify), approval gating, and sandbox isolation.
Does not stop (deliberate evasion)
Input-side fingerprint detection is fundamentally evadable by a model that is trying to exfiltrate: it can encode / transform a secret (base64, hex, String.fromCharCode, chunking) or route it through a channel Vigils doesn’t mediate (e.g. a Playwright-driven browser typing character-by-character). Vigils raises the bar and leaves an audit trail, but is not a guarantee against an adversarial agent. Don’t treat “Vigils is installed” as license to let an untrusted agent handle real credentials.
The complete fix (roadmap)
An egress proxy that mediates all outbound data is the full answer — it catches encoded / chunked exfiltration before the value leaves the machine. That’s future work; until then, treat Vigils as audit + accidental-leak prevention + reversible redaction, not a sealed exfiltration barrier.
Project status
| Dimension | State |
|---|---|
| Releases | 3-platform signed installers + auto-update (OTA) — latest release |
| Rust SDK | vigil-sdk published to crates.io |
| Security | Comprehensive audit (OWASP + STRIDE + supply chain) — 9.9 / 10, 0 critical / high |
| Maturity | Core safety claims proven-safe with code + test evidence; all sandbox / SDK / audit changes reviewed |
Distribution
- Desktop installers — Linux deb / rpm / AppImage + macOS dmg + Windows nsis / msi (Ed25519-signed, auto-update).
- Rust SDK —
cargo add vigil-sdk(crates.io / docs.rs). - Browser extension — Chrome MV3 (redacts before paste / submit on AI sites).
- CLI agent gateway —
vigil-hub serve --stdio; turnkeysetup --allcovers Claude Code / Codex / Gemini CLI / Cursor hooks and MCP wrapping for Claude Code / Codex / Cursor / Windsurf / Kimi CLI / ZCode / pi.
License
Apache-2.0 © Vigils Project Contributors
Quick links
- Installation
- Quickstart — Desktop App
- Agent Integration & Test
- Quickstart — Embedding the SDK
- Architecture Overview
Watch the demo (20 seconds)
One command — vigil-hub demo — shows the whole idea: your AI agent tries to leak a
secret, and Vigils stops it. Everything below runs locally: the firewall, the
redaction, and the tamper-evident audit are the real runtime code paths; only the
external model/tool is simulated, and no LLM is contacted.
Player not loading? Download the recording and play it with
asciinema play vigil-demo.cast, or just run it yourself after installing:vigil-hub demo.
What you just watched
- Default-deny. The agent puts a raw GitHub token into a tool call. The firewall
refuses to forward a raw secret —
DENY. - The Vigils way. The agent instead passes a placeholder (
secret://github_pat). The remote model only ever sees the placeholder; the real value is detokenized only at the local tool boundary; and when the tool’s result leaks a credential back, Vigils re-redacts it before the model sees anything. - Tamper-evident audit. Every step lands in a SHA-256 hash-chained ledger — with no
plaintext secrets stored. (
vigil-hub demo --tamperalters one row and the chain verification fails, on purpose.)
The agent did useful work with a real secret — while the model, the logs, and the audit never received the real value. That is the whole product, in one command.
Next: install it and protect your own agents with
vigil-hub setup --all.
Install Vigils
简体中文:安装 Vigils
1. Which download do I want?
| I want to… | Download | Where |
|---|---|---|
| Use the app — see activity, approve actions (a window/GUI) | the desktop installer for my OS | §2 |
| Guard my AI coding agent — Claude Code / Codex / Cursor / Zed | the CLI (vigils-cli-…) | §3 |
| Guard pasting into AI websites — ChatGPT / Claude / Gemini | the browser extension | §4 |
Most people want the desktop app. Developers wiring up an agent want the CLI.
All files live on the Releases page
(replace <version> below with the one you grabbed, e.g. 0.3.0).
2. Desktop app
Pick your OS, download, run. First launch shows a one-time “unknown developer” prompt (we’re not OS-code-signed yet) — the steps to allow it are below.
Windows
- Download
Vigils_<version>_x64-setup.exe→ double-click. - If a blue “Windows protected your PC” box appears → More info → Run anyway.
- Done. (The warning fades on its own as more people install.)
macOS
- Download the dmg for your chip — Apple Silicon:
Vigils_<version>_aarch64.dmg; Intel:Vigils_<version>_x64.dmg(not sure? Apple menu → About This Mac — “Apple M…” means Apple Silicon) → open it → drag Vigils to Applications. - First launch is blocked (“Apple could not verify…”). To allow it once: ** System Settings → Privacy & Security → scroll down → “Open Anyway” → confirm with Touch ID / password.**
- Done — it won’t ask again.
Terminal alternative (power users):
xattr -dr com.apple.quarantine /Applications/Vigils.appThis is the standard one-time step for any not-yet-notarized app; signing is planned.
Linux (Ubuntu 22.04+ / Debian 12+)
sudo dpkg -i Vigils_<version>_amd64.deb # Debian / Ubuntu
sudo rpm -i Vigils-<version>-1.x86_64.rpm # Fedora / RHEL
chmod +x Vigils_<version>_amd64.AppImage && ./Vigils_<version>_amd64.AppImage # portable, any distro
On an older distro (the desktop GUI needs glibc ≥ 2.35)? Use the CLI instead — it runs on practically any Linux (glibc ≥ 2.17).
3. CLI — guard your coding agent (30 seconds)
Install it with one line:
curl -fsSL https://vigils.ai/install.sh | sh # macOS / Linux
irm https://vigils.ai/install.ps1 | iex # Windows (PowerShell)
(Or download vigils-cli-<os> from Releases and unpack it — it contains vigil-hub.)
Then turn on protection:
vigil-hub setup # auto-detects Claude Code / Codex / Cursor and wires the guard
Restart your agent — that’s it. Raw secrets are now blocked from its tool calls, and every
block is recorded in a tamper-evident local ledger. Kick the tires with vigil-hub demo
(zero setup). Per-agent details: Agent Integration
(中文).
ML variant (optional, semantic PII + prompt-injection detection): download
vigils-cli-ml-<os>instead and runvigil-hub serve --engine ml. Floors: Linux glibc ≥ 2.28, macOS ≥ 14, Apple Silicon only (upstream onnxruntime dropped x86_64 macOS; Intel macs use the default hard-fingerprint engine). See Privacy Filter.
4. Browser extension (Chrome)
Redacts secrets / PII before you paste or submit on AI sites. Install
Vigils Browser Guard
from the Chrome Web Store — consumer mode works out of the box, no desktop app required.
Optional deep redaction: install the Vigils CLI, register the native messaging host
(vigil-native-host install --extension-id <ID>, ID shown in the extension’s Options),
then pick “Local Vigils engine” under enterprise mode in Options — checks route to the
local engine (hard fingerprints + ML PII when the daemon runs); raw text never leaves
the device. Dev builds:
chrome://extensions → Developer mode → Load unpacked → pick extensions/chrome-mv3/.
Worried it’s authentic? Every download has a SHA-256 checksum and a cryptographic build attestation — Verifying your download (中文). Building Vigils into your own Rust app? SDK Quickstart.
Verifying your download
🌐 中文版:验证你的下载
Vigils sits in front of your secrets, so you shouldn’t have to take our word that a download is genuine. Every release artifact is independently verifiable. This page shows how — and is honest about what each check does and doesn’t prove.
TL;DR
# The strongest check: proves the file was built by THIS repo's CI from THIS repo's
# source. Works for every artifact — CLI archives, desktop installers, extension zip.
gh attestation verify <downloaded-file> --repo duncatzat/vigils
A ✓ with a matching source repository and workflow means the binary is authentic; nothing else is strictly required. The sections below add complementary checks and explain the current absence of OS code-signing.
1. Build provenance — recommended, covers everything
Every downloadable artifact ships with a SLSA build-provenance attestation, signed through GitHub’s Sigstore-backed infrastructure (no key for you to manage, nothing to trust besides GitHub and the public source):
- CLI archives —
vigils-cli-linux-x64.tar.gz,vigils-cli-macos-arm64.tar.gz,vigils-cli-macos-x64.tar.gz,vigils-cli-windows-x64.zip - Desktop installers —
.exe,.msi,.dmg,.deb,.rpm,.AppImage - Browser extension —
vigils-chrome-extension.zip
Verify with the GitHub CLI:
gh attestation verify Vigils_0.1.7_amd64.deb --repo duncatzat/vigils
A pass proves the file was produced by this repository’s release workflow from a specific commit — i.e. it wasn’t swapped out, rebuilt by someone else, or altered after the build. This is a stronger statement about origin than a code-signing certificate alone, because it binds the binary to the exact public source commit and CI run you can go read.
Air-gapped? Download the artifact’s attestation bundle from the release and verify offline with
gh attestation verify <file> --bundle <bundle.jsonl> --repo duncatzat/vigils.
2. Checksums — CLI archives
Each CLI archive is published next to a .sha256 file. The one-line installer
checks it for you; to verify by hand:
# macOS / Linux — the .sha256 holds "<hash> <filename>"
shasum -a 256 -c vigils-cli-linux-x64.tar.gz.sha256
# Windows — compare against the published hash in vigils-cli-windows-x64.zip.sha256
(Get-FileHash -Algorithm SHA256 vigils-cli-windows-x64.zip).Hash
A checksum only proves the bytes you got match the bytes the release published (it
catches truncation / transport corruption). It is not a signature — use provenance
(§1) for authenticity. Desktop installers rely on provenance rather than a separate
.sha256.
3. The install script
The one-liner is a plain, readable shell/PowerShell script — read it before running. Note it verifies the archive checksum for you:
curl -fsSL https://vigils.ai/install.sh # read it first
curl -fsSL https://vigils.ai/install.sh | sh # then run
irm https://vigils.ai/install.ps1 # read it first
irm https://vigils.ai/install.ps1 | iex # then run
4. About the “unsigned app” warning
Vigils installers are not yet OS-code-signed or notarized — that requires a paid, identity-verified certificate from Apple and a Windows CA, which isn’t in place yet. So on first run your OS will warn you:
- macOS — Gatekeeper blocks the app. Verify provenance (§1) first, then clear the
quarantine flag:
xattr -d com.apple.quarantine /Applications/Vigils.app. - Windows — SmartScreen warns. Verify provenance (§1) first, then More info → Run anyway.
Until code-signing lands, build provenance is the verification path — and for proving a binary really came from this open source, it’s the stronger guarantee anyway.
5. Auto-updates are signed
Once installed, the desktop app’s auto-updater only accepts updates carrying a valid
signature from the project’s minisign key (the .sig files in each release), so the
update channel is authenticated end-to-end with no action from you. See
Auto-Update.
SDK Quickstart
[dependencies]
vigil-sdk = "0.1"
Hello, redaction
Hard-fingerprint PII detection — zero model dependencies:
use vigil_sdk::prelude::*;
fn main() {
let result: RedactionResult = scan_text(
"API: ghp_0123456789abcdefghijklmnopqrstuvwxyz12"
).unwrap();
for f in &result.findings {
println!("{:?} @ {:?}", f.kind, f.span);
}
// Output: github_token @ (5, 45)
}
Firewall + approval
#![allow(unused)]
fn main() {
use vigil_sdk::prelude::*;
let firewall = Firewall::new(FirewallConfig::default());
match firewall.evaluate(&invocation, &decision) {
Ok(FirewallOutcome::Allow) => proceed(),
Ok(FirewallOutcome::ApprovalRequired(req)) => queue(req),
Err(e) => return Err(e), // FAIL CLOSED
}
}
Invariants
- Fail-closed — errors → DENY.
- No-plaintext audit.
DecisionRecordmandatory.- API stability — 0.x minor evolution allowed; v1.0 freezes the surface.
See Invariants.
Feature flags
| Feature | Default | Description |
|---|---|---|
ort | off | ONNX-Runtime PII scanner (3-engine ensemble) |
Desktop Quickstart
First launch
The window opens on the Protection Overview — the Aegis command deck: a shield hero surrounded by an eight-emblem defense ring (firewall, approval, policy, sandbox, lease, redaction, gateway, audit). On a fresh machine it honestly reports no protection yet and offers the two one-click paths:
- Deploy Guardian — wires protection into every detected agent (per-agent status shown).
- Download engine — if the
vigil-hubengine isn’t on this machine yet, fetches it securely (HTTPS + SHA-pin against the signed engine manifest + run-verify, fail-closed).
Closing the window hides Vigils to the system tray — it keeps guarding in the background. Reopen or quit from the tray menu; launching the app again focuses the existing instance instead of starting a second one.
Connect an AI agent
The fastest path is the CLI turnkey (the desktop app’s Deploy Guardian button drives the same engine):
vigil-hub setup --all
This registers native-tool hooks (Claude Code / Codex / Gemini / Cursor) and rewrites each detected agent’s stdio MCP servers through Vigils (Claude Code / Codex / Cursor / Windsurf / Kimi CLI / ZCode / pi). Full matrix: Agent Integration.
Manual MCP stdio entry (any MCP-capable agent):
{ "mcpServers": { "vigil": { "command": "vigil-hub", "args": ["serve", "--stdio"] } } }
Browser extension
Chrome MV3 extension → native host → the same local engine and ledger the desktop app shows.
Pages at a glance
| Page | What it shows |
|---|---|
| Protection Overview | Shield hero + defense ring, per-agent guardian status, browser-guard card, one-click deploy |
| Approval Queue | Risky effects awaiting a decision — approve / reject, scoped grants |
| Activity Feed | Live audit stream, SQLite FTS5 search, hash-chain verification |
| Privacy Findings | What redaction caught — classes, counts, trends (no plaintext) |
| Server Registry | Active / Pending / Removed servers, descriptor drift |
| Session Replay | The full decision timeline for a chosen session_id |
| Settings | Engine mode (hardfp / ml / auto), posture, resident daemon, ML model install, audit checkpoint anchor |
See Architecture.
Agent Integration & Test Guide
🌐 中文版:Agent 接入与测试指南
Put Vigils in front of your AI agent’s tools, so every tool call your agent makes is firewalled (default-deny), audited (tamper-evident hash chain), redacted (secrets / PII), and — when risky — sent to approval. Everything runs locally; nothing leaves your machine.
Works with any MCP-capable agent: Claude Code, Codex, Cursor, Zed, Kimi CLI, ZCode, pi, OpenCode, Continue, and more.
How it works
Vigils runs as an MCP gateway: your agent connects to vigil-hub over stdio, and vigil-hub
proxies your real MCP tool servers (“upstreams”), gating every call.
┌──────────────────┐ stdio JSON-RPC ┌────────────────────┐ ┌──────────────────┐
│ Your agent │◄──────────────────►│ vigil-hub serve │─────►│ Upstream MCP │
│ Claude Code / │ │ --stdio │ │ servers │
│ Codex / Cursor /│ │ ┌──────────────┐ │ │ (filesystem, │
│ Zed / ... │ │ │ Firewall │ │ │ github, db, ...) │
└──────────────────┘ │ │ Audit ledger │ │ └──────────────────┘
│ │ Redaction │ │
│ │ Approval │ │
│ └──────────────┘ │
└────────────────────┘
Each upstream’s tools are namespaced with a __ (double-underscore) separator — <server>__<tool>,
e.g. fs__read_file, github__create_issue — and aggregated into the tools/list your agent sees. When the agent calls one, Vigils evaluates it against the
firewall before forwarding, records a decision in the audit ledger, and either allows it,
denies it, or queues it for your approval.
Prerequisites
Install the CLI gateway, vigil-hub:
- Prebuilt: download
vigils-cli-<target>.tar.gz(.zipon Windows) from the latest release — it containsvigil-hubandvigil-native-host. Putvigil-hubon yourPATH. - From source:
cargo install --path apps/vigil-hub-cli
Verify: vigil-hub --help
Fastest path — Claude Code, one command (setup --all)
If you use Claude Code, you don’t need to hand-write configs at all. One command detects your setup and protects everything — your existing config is backed up and only Vigils’ own entries are added (fully reversible):
vigil-hub setup --all
setup --all wires up both layers:
- Native-tool input guard — a
PreToolUsehook so every tool call (Bash, Edit, Write, Read, MCP tools, …) is checked before it runs; a real credential heading into a tool is blocked fail-closed and audited. Claude Code and Codex also get aUserPromptSubmithook, so a bare credential pasted into the prompt is blocked before it reaches the model. - MCP gateway — rewrites each of your stdio MCP servers to run through Vigils, so secrets in
tool results are scrubbed before the model sees them and every call is audited. Defaults to
monitor posture (your servers stay usable; raw-secret block, result redaction, and audit
stay on). Add
--enforcefor default-deny gating.
vigil-hub setup --mcp --doctor # pre-flight: will each wrapped MCP server actually start? (read-only PATH check)
vigil-hub verify # after using your agent: confirm the tamper-evident audit chain is intact (the desktop app shows what was caught)
vigil-hub setup --all --uninstall # remove everything (config restored byte-for-byte)
Restart Claude Code and you’re protected.
What setup auto-detects (2026-07):
| Agent | Hook (native-tool gate) | MCP wrap (server gateway) | Config touched |
|---|---|---|---|
| Claude Code | ✅ | ✅ (user + local scope) | ~/.claude/settings.json + ~/.claude.json |
| Codex CLI | ✅ (one-time /hooks approval inside Codex — pending trust until you approve; headless / CI automation can run the hooks with Codex 0.154+ codex exec --dangerously-bypass-hook-trust, only where hook sources are already vetted) | ✅ (honors $CODEX_HOME) | $CODEX_HOME/hooks.json + config.toml |
| Gemini CLI | ✅ | — (MCP lives inside the shared settings.json; later increment) | ~/.gemini/settings.json |
| Cursor | ✅ | ✅ | ~/.cursor/hooks.json + mcp.json |
| Windsurf | — | ✅ | ~/.codeium/windsurf/mcp_config.json |
| Kimi CLI | — (hooks are Beta and officially fail-open; deferred until GA) | ✅ | ~/.kimi/mcp.json |
| ZCode | — (plugin-only hook surface) | ✅ (close ZCode before --apply) | mcp.servers inside ~/.zcode/cli/config.json |
| pi | — (no hook mechanism) | ✅ (via the pi-mcp-adapter convention file — pi ships no built-in MCP) | $PI_AGENT_DIR/mcp.json (default ~/.pi/agent/) |
ZCode notes: the workspace-level
<project>/.zcode/config.jsonis an in-repo file (may be committed) and is deliberately left alone; the shared~/.agents/mcp.jsonfallback is not rewritten either (multi-host file, identity ownership unsettled). Users who configure MCP in ZCode’s settings panel (which always writes back to the user-level.zcodeconfig) get full protection. For pi, a missingmcp.jsonsimply means “nothing to protect” — the file is never created for you.
The rest of this guide is the manual path — use it
for non-Claude agents (Codex / Cursor / Zed / …) or when you want explicit control over the
serve gateway and its upstreams.json.
Step 1 — Smoke-test vigil-hub (30s, no agent needed)
Confirm the gateway speaks MCP before wiring any agent. Pipe an initialize + tools/list into
it (MCP stdio is newline-delimited JSON-RPC):
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| vigil-hub serve --stdio --ledger ./vigil.db
Expected stdout (two JSON-RPC responses):
{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":false}},"protocolVersion":"2025-06-18","serverInfo":{"name":"vigil-hub","version":"0.1.7"}}}
{"id":2,"jsonrpc":"2.0","result":{"tools":[]}}
tools/list is empty because no upstreams are configured yet (next step). Startup banners go to
stderr (stdout is reserved for the protocol).
Step 2 — Declare your tool servers (upstreams.json)
List the MCP servers you want Vigils to proxy. Bare commands resolve via PATH.
{
"upstreams": [
{ "name": "fs", "argv": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/data"] },
{ "name": "github", "argv": ["npx", "-y", "@modelcontextprotocol/server-github"] }
]
}
Pass it to serve:
vigil-hub serve --stdio --ledger ./vigil.db --upstream-config ./upstreams.json
For each entry Vigils registers the server, pins its launch command, and runs a
gate-before-spawn check (argv + resolved-program drift) before starting the child process —
then namespaces its tools (fs__…, github__…) into tools/list.
HTTP / remote MCP servers use OAuth onboarding instead:
vigil-hub add-remote-mcp --url https://mcp.example.com/ --client-id <id> --scopes mcp:tools.read
Step 3 — Point your agent at vigil-hub
Use a shared ledger path so the desktop app and CLI see the same audit trail. The desktop app
reads data_local_dir()/Vigil/ledger.sqlite3:
- Windows:
%LOCALAPPDATA%\Vigil\ledger.sqlite3 - Linux:
~/.local/share/Vigil/ledger.sqlite3 - macOS:
~/Library/Application Support/Vigil/ledger.sqlite3
The filename must be exactly ledger.sqlite3 (with the 3) and the directory must be
data_local_dir() — a path mismatch means the desktop reads a different file and the
Activity Feed stays empty. Easiest path: run vigil-hub setup --mcp / setup --all, which
auto-targets this shared ledger. Do not omit --ledger on a manual serve --stdio — that
uses an in-memory ledger the desktop can’t see. serve/wrap print the resolved ledger path
on startup, so you can compare it with the desktop’s.
In the snippets below, replace the --ledger / --upstream-config paths and the vigil-hub path
(use the absolute .exe path on Windows, e.g. C:\\Vigil\\vigil-hub.exe).
Claude Code
Project .mcp.json (or user-level ~/.claude.json → mcpServers):
{
"mcpServers": {
"vigil": {
"command": "vigil-hub",
"args": ["serve", "--stdio", "--ledger", "~/.local/share/Vigil/ledger.sqlite3", "--upstream-config", "./upstreams.json"]
}
}
}
Run /mcp in Claude Code — vigil should show connected, with your upstream tools under it.
Codex (OpenAI Codex CLI)
~/.codex/config.toml (or project .codex/config.toml):
[mcp_servers.vigil]
command = "vigil-hub"
args = ["serve", "--stdio", "--ledger", "~/.local/share/Vigil/ledger.sqlite3", "--upstream-config", "./upstreams.json"]
Cursor
~/.cursor/mcp.json (or project .cursor/mcp.json):
{ "mcpServers": { "vigil": { "command": "vigil-hub", "args": ["serve", "--stdio", "--upstream-config", "./upstreams.json"] } } }
Zed
~/.config/zed/settings.json:
{ "context_servers": { "vigil": { "command": { "path": "vigil-hub", "args": ["serve", "--stdio", "--upstream-config", "./upstreams.json"] } } } }
OpenCode
Project opencode.json:
{ "mcp": { "vigil": { "type": "local", "command": ["vigil-hub", "serve", "--stdio", "--upstream-config", "./upstreams.json"], "enabled": true } } }
Continue (VS Code / JetBrains)
~/.continue/config.yaml:
mcpServers:
- name: vigil
command: vigil-hub
args: ["serve", "--stdio", "--upstream-config", "./upstreams.json"]
Step 4 — Verify it’s actually gating
After your agent runs a tool call (or trigger one yourself), inspect the local ledger. inspect
prints single-line JSON — pipe to jq:
vigil-hub inspect --db-path ./vigil.db protection # at-a-glance: secrets blocked, leaks redacted, chain intact
vigil-hub inspect --db-path ./vigil.db activity --limit 20 # recent events / decisions
vigil-hub inspect --db-path ./vigil.db search "read_file" # full-text search the trail
vigil-hub inspect --db-path ./vigil.db approvals list # risky calls awaiting you
vigil-hub inspect --db-path ./vigil.db verify-chain # tamper-evident chain check
# → {"kind":"ChainVerification","data":{"ok":true,"broken_at_event_id":null,"message":null}}
Or open the Vigils desktop app for a live view: Activity Feed, Approval Queue (approve / deny), Server Registry, Session Replay, and Privacy Findings.
What “gating” looks like: with the default firewall (deny-by-default), a risky tool call is
either denied outright or surfaced in the Approval Queue — your agent’s call blocks until you
approve. The decision is recorded in activity.
Optional — turn on the ML privacy filter
By default Vigils uses fast hard-fingerprint rules (no ML). To add the ONNX PII scanner, build the
CLI with the ort feature and pass --enable-privacy-filter:
cargo install --path apps/vigil-hub-cli --features ort
vigil-hub serve --stdio --upstream-config ./upstreams.json --enable-privacy-filter
If the flag is set but the binary wasn’t built with --features ort, startup fails closed (it
never silently runs without the filter you asked for).
Troubleshooting
command not found/ agent can’t start vigil-hub — use the absolute path tovigil-hub(vigil-hub.exeon Windows) in the config; verify withvigil-hub --version.- Agent connects but no tools — you haven’t passed
--upstream-config, or the file lists no upstreams. Add yourupstreams.json. - An upstream fails to start — make sure its
argvruns standalone and thatnpx/node(or whatever it needs) is onPATH. - Desktop app doesn’t show events — point
--ledgerat the same path the desktop app uses (Step 3), writable by the agent’s child process. - Garbled bytes in the agent log — nothing but JSON-RPC may go to stdout;
vigil-hubkeeps all banners on stderr.
References
- Architecture · MCP Hub · Action Firewall
apps/vigil-hub-cli/src/serve.rs— theserveimplementation
Architecture Overview
Vigils is a 5-layer control plane (T0 → T4):
T4 — Desktop UI / Browser Extension / CLI
T3 — MCP Hub (server registry, descriptor pinning)
T2 — Action Firewall + Approval Queue + Policy + Audit Ledger
T1 — Privacy Filter + Sandbox Runner (Wasm + native)
T0 — Types + Schemas (vigil-types)
Crate layout
SDK boundary (publishable, 10 crates)
| Layer | Crate |
|---|---|
| T0 | vigil-types |
| T0 | vigil-redaction (hard rules + optional ORT) |
| T0 | vigil-runner-types |
| T2 | vigil-policy / vigil-audit / vigil-lease / vigil-firewall |
| T3 | vigil-ui-protocol / vigil-mcp |
| — | vigil-sdk (facade) |
Internal (not published)
vigil-runnerconcrete (wasmtime + sandbox-linux + vigil-redaction deps)vigil-sandbox-linux(Linux-only, target-gated)vigil-daemon-ipc(daemon wire protocol + thin client, shared by hook / native-host)vigil-http-auth/vigil-http-transport/vigil-browserapps/desktop/apps/native-host/apps/vigil-hub-cli
Key invariants
- Fail-closed errors → DENY
- No-plaintext audit
DecisionRecordmandatoryinherit_env=false(native) + fuel/epoch dual limit (Wasm) + Landlock (Linux)- preopen allowlist only / per-run independent engine
See the ADR Index for the design rationale behind each layer.
Data flow (typical action)
1. AI agent → MCP request → vigil-hub stdio
2. vigil-hub::descriptor_lookup → drift check
3. vigil-firewall::evaluate → policy + scope + PII
4. (if approval needed) → ApprovalBroker → Desktop UI
5. (on approve) vigil-runner::spawn_native/wasm → sandbox
6. vigil-audit::append → SQLite ledger
7. result → AI agent
Every step is fail-closed: any error → DENY plus a ledger event.
Action Firewall
vigil-firewall::Firewall::evaluate(invocation, decision) → FirewallOutcome
A fail-closed effect-gating API with three outcomes:
- Allow — policy, privacy, and scope checks all pass.
- ApprovalRequired — a risky effect is queued for a human.
- Deny — policy block, PII detected, or scope outside the allowlist.
Policy DSL
OAuth scope allowlists are enforced at the firewall layer (see ADR 0011): a request whose granted scopes fall outside the configured allowlist is denied by default.
PII scanner integration
Two layers of defense — hard fingerprint rules plus an optional ONNX ensemble. See Privacy Filter and ADR 0013.
Audit Ledger
vigil-audit — an append-only ledger protected by a SHA-256 hash chain:
event_n.hash = sha256(event_{n-1}.hash || serde_jcs(event_n) || timestamp_n)
serde_jcs (RFC 8785 canonical JSON) keeps the hash consistent across implementations.
Since the 2026-06 security audit the chain digest is versioned (v2) and additionally binds
session_id,event_type, andredacted_text, so a local actor with database write access can no longer rewrite those columns undetected. Historical v1 events stay verifiable, andverify_chainenforces version monotonicity (a v2→v1 downgrade is rejected). See the ADR Index and the security advisory.
Storage
SQLite (WAL) + FTS5. Schema: vigil-audit/migrations/.
Invariants
- Append-only (no
UPDATE/DELETE). - No-plaintext (raw secrets are never stored).
- SHA-256 chain (tamper-evident).
- FTS5 search by
event_type/session_id.
See ADR 0001 and ADR 0005.
Privacy Filter
vigil-redaction provides two layers of defense in depth.
Layer 1 — Hard fingerprints
Fixed-prefix and structured-credential rules with 100% precision, zero ML dependency, and instant startup:
| Kind | Pattern |
|---|---|
| github_token | ghp_ / gho_ / ghu_ / ghs_ / ghr_ + 36; github_pat_ + 50 (fine-grained PAT) |
| env_assignment | KEY=value free text (whole segment); "password": "…" / 'api_token' => '…' quoted credential keys (JSON / PHP / YAML / JS, value-only redaction); 密码:… Chinese keywords |
| slack_webhook | hooks.slack.com/services/T... |
| slack_token | xox[baprs]- + 10+ (v6) |
| stripe_secret | sk_live_ / sk_test_ |
| google_api_key | AIza + 35 |
| gitlab_pat | glpat- + 20 |
| aws_access_key | AKIA + 16 uppercase |
| aliyun_access_key_id | LTAI + 12–20 (Alibaba Cloud AccessKey ID, v6) |
| tencent_secret_id | AKID + 32 (Tencent Cloud SecretId, v6) |
| huggingface_token | hf_ + 30+ (v6) |
| database_url | <scheme>://user:password@host/db |
| private_key | PEM block |
| … | 17 kinds total |
Layer 2 — ONNX model (opt-in: the ML release build, or --features ort)
⚠️ Honest boundary: the default
vigils-cli-<plat>build runs Layer 1 (hard fingerprints) only. Layer 2 (the ML model) needs (1) an ort-enabled binary — either the prebuiltvigils-cli-ml-<plat>release build or your owncargo build --features ort, (2)vigil-hub serve --engine ml(orauto) at runtime, and (3) the model files (fetched on first use; ~0.8–1.5 GB, Hugging Face + vigils.ai mirror, SHA-256 verified). If any is missing, behavior falls back to Layer 1 — see the table below and ADR 0022. The ML release build bundles the ONNX Runtime 1.24 dylib and floors at Linux glibc ≥ 2.28 / macOS ≥ 14.
OpenAI Privacy Filter (PII NER) + a DeBERTa prompt-injection classifier (soft signal); ort
builds additionally carry a multilingual ensemble (xlmr / yonigo). Typical latency: cold
~7–11 s, warm p95 ~420 ms — inference runs synchronously in the gateway preflight, which is
exactly why --engine lets latency-sensitive deployments stay Layer-1-only.
A per-(language, label) threshold profile calibrates recall vs. false positives — for example
tightening zh.account_number cut a noisy false-positive cluster while improving F1.
Engine selection — --engine (ADR 0022)
vigil-hub serve --engine <hardfp|ml|auto>, default hardfp:
--engine | Layer 2 ML | When the model / dylib is missing |
|---|---|---|
hardfp (default) | off | — (hard fingerprints only) |
ml | on (strict) | refuses to start — if you ask for ML it must be available, no silent fallback |
auto | on (if already cached) | degrades to hard fingerprints + warns — never triggers a download |
Coexistence is structural: hard-fingerprint hits are always kept; the ML layer only adds non-overlapping findings (ADR 0013 merge) — so credentials covered by Layer 1 are never missed, ML on or off.
Outbound Gate
Opt-in. A loopback HTTP proxy for the model API itself. Agents are pointed at it; every request body is scanned for hard-secret fingerprints and hits are replaced before the request leaves the machine. Responses stream through untouched.
$ vigil-hub outbound on # writes the agent configs, persists the switch
$ vigil-hub daemon start # the gate runs inside the daemon
$ vigil-hub outbound off # restores the agent configs (only keys Vigil wrote)
Why
The hook and the MCP gateway both sit at the entrance — tool inputs and tool results. Neither sees
@file inlining, memory files (CLAUDE.md / AGENTS.md), compaction summaries, or a credential the
model itself repeats back. And because the context is append-only, a secret that got in is re-sent on
every subsequent model call for the life of the session. The outbound gate is the only layer that
sees the bytes actually leaving the machine.
Wiring
| Agent | File | Keys written |
|---|---|---|
| Claude Code | ~/.claude/settings.json | env.ANTHROPIC_BASE_URL, env.ENABLE_TOOL_SEARCH |
| Codex CLI | <CODEX_HOME>/config.toml | model_provider, [model_providers.vigil] |
Backed up, atomically replaced, TOCTOU-guarded; no other key is touched and TOML comments survive.
An existing ANTHROPIC_BASE_URL pointing at your own gateway is remembered and chained behind the
gate, then restored on off. Subscription OAuth keeps working — Authorization / x-api-key /
ChatGPT-Account-ID are forwarded byte-for-byte and never logged.
Request handling
- Read the full body (413 above the limit).
- Refuse what cannot be inspected: non-JSON bodies (415) and compressed bodies. Read-only methods pass straight through.
- Rewrite every JSON string leaf through the same 17 hard-fingerprint rules the hook uses
(
vigil_redaction::hard_secret_spans), replacing hit spans with[REDACTED <kind>]. - Skip protocol envelope keys (
id,*_id,model,role,signature,encrypted_content,thinking, …) — rewriting those breaks the protocol or achieves nothing. Inline binary payloads get no exemption: a media type is the caller’s own claim, and honouring it would hand anyone a free pass by wrapping a credential in one. Real image bytes are not text and never match a rule. - Re-scan the bytes about to be sent. Rewriting works on the parsed tree; what goes upstream is bytes. A skipped envelope key, a duplicate JSON key, or a placeholder-stripping mismatch can carry a credential all the way here — a hit at this point is a 422 refusal, not a forward.
- Forward with headers intact (minus hop-by-hop), then relay the response chunk by chunk. SSE is never buffered or parsed.
Rewrites are audited locally (outbound.request.redacted) with rule names and counts only — never
any original bytes.
Honest boundary
This is not a reversible-masking proxy. Only credential-shaped hard fingerprints are handled, and
they are not restored: the model sees [REDACTED …], and no plaintext mapping table is kept
anywhere. Also:
- Only the model-API leg. Tool traffic to other hosts belongs to the hook and the gateway.
- Hard fingerprints only — semantic PII is the ML engine’s job, and deliberately encoded or cross-call-split exfiltration still gets through.
- The outbound byte self-check is not a second, independent detector. It runs the same rules, so it inherits the same blind spots: a 256-run scanning budget per body that plain non-credential base64 strings can exhaust, single-level decoding only, and text-only payloads. What it does cover is the case where the rewritten tree and the bytes on the wire disagree.
- One known, constructible gap, named rather than glossed: put enough long non-credential base64 runs early in the body, then a base64-encoded credential later, and the early runs exhaust the scanning budget so the credential is never examined and the request is forwarded. Padding and payload need not share a field. A test asserting the 422 exists and is currently red on purpose, marked ignored so the gap stays visible; the fix belongs to the shared redaction crate. Ordinary traffic is unaffected, but do not treat the gate as a guarantee that credentials cannot leave.
- Response bodies are not scanned.
- Bedrock / Vertex / Foundry modes are refused (they do not use the base URL; Bedrock signs the body).
- Codex with a custom
model_provideris refused; Gemini CLI is not wired yet. - Codex’s compaction / memories backend routes do not pass through the gate — Codex reserves those for
the built-in
OpenAIprovider whose URL ends in/backend-api/codex. Sampling requests, the ones carrying your context, still go through. requires_openai_auth = truecovers users authenticated throughcodex login(subscription or API key). A bareOPENAI_API_KEYenv var with no prior login is not silently used; Codex asks you to log in.- Cloud / remote sessions are invisible — that traffic is not on this machine.
- While the gate is down, agents pointed at it fail closed rather than silently bypassing it.
The listen address must be loopback; a non-loopback address in the config is treated as corrupt and the gate stays off.
MCP Hub
vigil-mcp — a Model Context Protocol hub that sits in front of your tool servers.
- Server registry — stdio servers and HTTP servers (OAuth / JWT; see ADR 0011).
- Descriptor pinning — a SHA-256 of each server’s tool list + schemas, used for drift detection. A changed descriptor is treated as first-seen (approval-required), never auto-trusted.
- Outbox — an append-only outbox table for reliable delivery.
- Approval-queue integration — the embedded Hub shares cross-process SQLite persistence with the Approval Queue (see ADR 0014).
See ADR 0004, 0005, 0011, and 0014.
Approval Queue
Human-in-the-loop gating for risky effects (driven by FirewallOutcome::ApprovalRequired):
firewall::evaluate → ApprovalRequired(req)
→ ApprovalBroker (SQLite, persistent)
→ Desktop UI "Approval Queue" page
→ user: Approve / Reject / Delegate / Defer
→ ledger event
→ vigil-runner spawn (only if approved)
ApprovalScope
Each request carries a scope (see ADR 0014):
server_id+tool_nameeffect_kind(read / write / network / …)resource_pathduration(once / session / forever)
Delegate hands the decision to a super-user or batch-approval flow.
See ADR 0003 and ADR 0014.
vigil-sdk Reference
A stable public facade that re-exports the vigil-types, vigil-firewall,
vigil-redaction, and vigil-mcp surfaces.
Public surface
#![allow(unused)]
fn main() {
use vigil_sdk::prelude::*;
// vigil-types
pub use vigil_types::{DecisionRecord, AuditEvent, EffectVector,
ApprovalRequest, ApprovalResolution, ApprovalScope, ApprovalStatus,
ToolInvocation, EffectKind, DecisionKind};
// vigil-firewall
pub use vigil_firewall::{Firewall, FirewallConfig, FirewallError, FirewallOutcome,
EngineStatusReport, PiiScanner, OAuthScopeContext};
// vigil-redaction
pub use vigil_redaction::{scan_text, RedactionResult, Finding, FindingKind, FindingSource};
// vigil-mcp
pub use vigil_mcp::descriptor_hash;
}
Out of scope (not exported)
- Server runtime (Hub / oracle internals).
- Backend implementations (
NoopEngine/MockEngine/OrtEngine). - Ops infrastructure (bootstrap / model distribution).
- The concrete
vigil-runner(WasmRunner/spawn_native).
See Invariants and docs.rs/vigil-sdk.
SDK Invariants
The SDK boundary is defined by ADR 0015. Consumers can rely on these guarantees.
1. Fail-closed
#![allow(unused)]
fn main() {
match firewall.evaluate(..) {
Ok(FirewallOutcome::Allow) => proceed(),
Ok(_) => other_handling(),
Err(e) => return Err(e), // NEVER default to allow
}
}
2. No-plaintext audit
Raw text passed to the SDK is never persisted. Audit records go through DecisionRecord /
AuditEvent (a hash plus a redacted body).
3. DecisionRecord mandatory
Every effect must first produce a DecisionRecord. No SDK API lets a consumer skip it.
4. API stability
- 0.x: additive improvements are allowed (each behind review + an ADR).
- After v1.0: additive only — no removals, no signature changes.
- Adding a variant/field to a
#[non_exhaustive]enum/struct is not a breaking change.
5. Reviewed surface changes
Every change to the SDK’s public surface is reviewed before release.
SDK Feature Flags
ort (default: off)
vigil-sdk = { version = "0.1", features = ["ort"] }
Enabled:
- The
ortcrate + ONNX Runtime 1.24.4 (dynamic library). - A 3-engine ensemble (OpenAI Privacy Filter +
xlmr-pii-v1+yonigo-pii-v1). - An 8-class
PrivacyLabel.
Disabled (default):
- 17 hard fingerprint rules.
- A
NoopEngineplaceholder. - No ONNX dependency; sub-second startup.
Choosing
| Scenario | Feature |
|---|---|
| CLI tool wrapper | default — hard rules cover the vast majority of leaks, instant |
| Long-running agent | ort — higher recall (~11 s cold + ~419 ms warm) |
| Browser extension | default — size- and cold-start-sensitive |
Runtime environment (with ort)
export ORT_DYLIB_PATH=/path/to/onnxruntime-<platform>-1.24.4/lib/libonnxruntime.so.1.24.4
export VIGIL_PRIVACY_FILTER_MODEL_DIR=/path/to/models/openai-pf/v1
See ADR 0012, 0016, and 0017.
Auto-Update
Installed desktop apps update over the Tauri auto-updater, using Ed25519 (minisign) signatures.
Status (2026-09): the shipped desktop app does not yet bundle the updater plugin, so no desktop client polls this endpoint today. The only client-side poll is the CLI’s daily update check (
serve/daemon start), which reuses the same manifest URL. The manifests, signing flow and endpoint below remain the release-side contract.
Flow
client startup
→ Tauri updater plugin polls
https://vigils.ai/desktop-updates/<target>-<arch>/<current_version>.json
→ server returns { version, url, signature, ... }
→ client compares server.version > local.version
→ downloads the bundle and verifies its .sig against the embedded public key
→ restarts on the new version
The update endpoint and signing key are operated by the project; release bundles are signed
in CI. macOS is served as two independent lines — darwin-aarch64 (Apple Silicon) and
darwin-x86_64 (Intel) — each with its own manifest and arch-suffixed updater archive, so an
update can never deliver the wrong architecture.
Building a signed bundle
export TAURI_SIGNING_PRIVATE_KEY="$(cat <your-updater-key>)"
cargo tauri build --features gui --config '{"bundle":{"createUpdaterArtifacts":true}}'
# produces the platform bundle plus a matching .sig
For the full pipeline (CI signing → manifest generation → endpoint sync), see
docs/ota-pipeline.md.
Daily update check (version ping)
In one sentence:
vigil-hub serveandvigil-hub daemon startmake at most oneGETper day tovigils.ai, carrying only your platform and the running version, to tell you when a newer release exists. The server counts those requests per day — that count is the project’s only adoption metric. No identifiers are sent, one command turns it off, and thehookpath never makes network calls.
Why it exists
- Update notice. The CLI has no auto-updater; you should learn about new releases, especially security fixes.
- Adoption count. Vigils has no product telemetry (no usage events, no crash reports). To decide whether the project is worth continuing we need exactly one number: how many installs run on a given day. Counting real update checks per day gives Active Daily Installs (ADI, the model Firefox used 2008–2020). This is the “update-check poll” the project FAQ has always described as its only outbound traffic.
- This is not the telemetry that Vigils’ opt-in principle covers (crash reporting with stack traces, never shipped). An update check is a different thing.
Exactly what is sent
One request, nothing else:
GET /desktop-updates/windows-x86_64/0.7.1.json HTTP/1.1
Host: vigils.ai
User-Agent: vigil-hub/0.7.1
windows-x86_64is the platform (darwin-aarch64,linux-x86_64, …);0.7.1is your vigil-hub version.User-Agentis product name + version (the desktop app will usevigils-desktop/<version>).- Not sent: device id, random id, weekly bucket, salt, hostname, username, paths, ledger contents, any configuration, any query string, any body.
- No redirects are followed; the manifest is read up to 64 KB; only its
versionfield is used, and only after it passes a strict SemVer check.
The integration test apps/vigil-hub-cli/tests/update_check.rs captures the request with a local one-shot HTTP
server and asserts every point above (one request, only those two fields, no extra headers, zero traffic under
each kill switch).
When it runs
| Entry point | Behaviour |
|---|---|
vigil-hub serve --stdio | decided at start-up, sent from a background thread; stdout is never touched (it is the MCP channel), only a stderr line |
vigil-hub daemon start | same, after model warm-up |
vigil-hub hook (every tool call) | never. Short-lived process with a latency budget; a source-level test asserts hook.rs / command_guard.rs / posture.rs do not reference the update check |
| any other subcommand | never |
- At most once per 24 hours per install: the only local state is a throttle file
<data_local>/Vigil/update-check.last(the Unix time of the last attempt). It is written before the request, so an offline or failed attempt is not retried within 24 hours. - Notice up front:
vigil-hub setup(apply-style runs) prints one stderr line before changing anything;serve/daemonprint one line before their very first attempt. Nothing is repeated after that. - No local data directory (nothing to throttle with) → no request. Better to under-count than over-send.
When a newer release exists, stderr gets one more line:
vigil-hub 0.7.1: a newer release 0.8.0 is available -> https://github.com/duncatzat/vigils/releases/latest
Turning it off
Any one of these is enough and overrides everything else:
| Switch | Notes |
|---|---|
vigil-hub version-ping off | writes <data_local>/Vigil/version-ping.off; on removes it |
VIGIL_NO_VERSION_PING=1 | product-specific environment variable (any non-empty value other than 0 / false / off / no) |
DO_NOT_TRACK=1 | the industry convention (https://consoledonottrack.com) |
Check once by hand and see the outcome, including failure reasons (refused while disabled — no exceptions):
vigil-hub version-ping check.
Status:
$ vigil-hub version-ping status
version ping: enabled
what: one GET per day from `serve` / `daemon start`, to notice a newer release
sends: platform + version only, no identifiers
request: GET https://vigils.ai/desktop-updates/windows-x86_64/0.7.1.json
last try: 3h ago
turn off: vigil-hub version-ping off (or VIGIL_NO_VERSION_PING=1 / DO_NOT_TRACK=1)
vigil-hub version-ping status --json prints a stable, language-independent schema: enabled, disabled_by
(env:VIGIL_NO_VERSION_PING / env:DO_NOT_TRACK / marker / no-state-dir / null), endpoint,
user_agent, sends, last_attempt_unix, min_interval_secs.
Self-hosting or an internal mirror: VIGIL_UPDATE_ENDPOINT=https://mirror.example.com (base URL; the path
rule is unchanged).
What the server keeps
- The origin is
vigils.ai(nginx behind Cloudflare)./desktop-updates/*isno-cache, so every request reaches the origin. - nginx keeps its default
combinedaccess log: time, request line, status, User-Agent, and the connecting IP — which, behind Cloudflare, is a Cloudflare edge node, not you (the origin does not restore the real client IP and does not logCF-Connecting-IP). Logs rotate daily and are kept for 14 days. - The aggregation script only outputs counts: requests per
(day, platform, version), the 7-day ADI mean, and platform / version breakdowns. The stored report and history contain no IPs and no raw User-Agents. - The aggregate is public at https://vigils.ai/stats/adoption.json (as Homebrew and Fedora do: the people being counted can see the number).
Legal framing (GDPR / ePrivacy)
- The client stores exactly one throttle timestamp, strictly necessary for the user-facing “update check” function; no salt, id or bucket (EDPB Guidelines 2/2023 treat any information written to terminal equipment as ePrivacy 5(3) territory, so the design narrows “storage” to that single file).
- The connecting IP the server sees transiently is processed under GDPR Art. 6(1)(f) legitimate interest. LIA summary — purpose: update notice and install counting; necessity: the data is already minimal (platform + version); balance: no identifiers, off switch, notice up front, public aggregate, logs deleted after 14 days.
- Public wording is: “no usage telemetry; only one counting-style update check that you can turn off” — not shorthand like “zero network requests”.
Scope
- Ships in the first release after the one whose release notes announce it.
- CLI only for now (
serve/daemon); the desktop app follows once it has a Settings toggle (its User-Agent will bevigils-desktop/<version>). - Code: policy crate
crates/vigil-update-check(pure logic, no networking, tested offline); wiring inapps/vigil-hub-cli/src/update_check.rs; tests inapps/vigil-hub-cli/tests/update_check.rs.
简体中文版本:update-check.zh-CN.md
Multi-Platform Build
Three platforms, four architectures (Windows x64 · Linux x64 · macOS Apple Silicon · macOS Intel).
Linux
sudo apt install libwebkit2gtk-4.1-dev libsoup-3.0-dev libgtk-3-dev
cd apps/desktop
cargo tauri build --features gui --bundles deb,rpm,appimage
macOS
export CI=true # skips the AppleScript Finder styling that needs a GUI session
cd apps/desktop
cargo tauri build --features gui --bundles app,dmg
# app target = updater artifact (.app.tar.gz + .sig)
# dmg target = user-facing installer
# Intel (x86_64) — cross-compile from the arm64 runner; the Apple SDK is dual-arch:
rustup target add x86_64-apple-darwin
cargo tauri build --features gui --target x86_64-apple-darwin --bundles app,dmg
# bundles land under target/x86_64-apple-darwin/release/bundle
In CI the two mac updater archives are renamed per-arch (Vigils-darwin-<arch>.app.tar.gz)
so they can coexist as release assets; each latest-darwin-<arch>.json points at its own.
Windows
cd apps/desktop
cargo tauri build --features gui --bundles msi,nsis
Binary layout
apps/desktop/Cargo.toml declares a single binary —
[[bin]] name = "vigils" path = "src/bin/vigils.rs" — aligned with the Tauri v2 bundle
default (the bin name matches the path basename and mainBinaryName). The installed
executable is vigils / vigils.exe (since v0.1.5; earlier builds shipped gui).
Architecture Decision Records
Every significant design decision is recorded as an ADR under
docs/adr/. Each ADR and its
implementation were reviewed before merge.
| # | Title |
|---|---|
| 0001 | Action control plane |
| 0002 | Audit ledger + hash chain (revised: v2) |
| 0003 | Firewall + approval queue |
| 0004 | MCP hub + outbox |
| 0005 | Descriptor pinning + drift |
| 0006 | Secret lease broker |
| 0007 | Sandbox runner (Wasm + native + Landlock) |
| 0008 | Desktop UI protocol |
| 0009 | Browser extension + native host |
| 0010 | HTTP MCP auth |
| 0011 | HTTP transport + JWKS |
| 0012 | Model distribution |
| 0013 | Hard × model merge |
| 0014 | Tauri-embedded Hub |
| 0015 | SDK boundary |
| 0016 | Performance gate |
| 0017 | Model descriptor design |
| 0018 | vigil-runner-types split |
| 0019 | Audit advisories policy |
| 0020 | Audit checkpoint anchor |
| 0021 | HTTP/SSE MCP upstream |
| 0022 | Engine selection + degradation |
| 0023 | Async injection preflight |
| 0024 | Resident daemon + hook ML |
| 0025 | Outbound LLM-API gate (opt-in) |
Changelog & Releases
Vigils ships from a single public version line (0.1.x).
- Binaries & signed installers — the GitHub Releases page.
- Full changelog —
CHANGELOG.md(简体中文).
Recent highlights
| Version | Theme |
|---|---|
| v0.1.7 | Security hardening — ports the first comprehensive audit’s fixes (hash-chain v2, descriptor-hash guard, …) |
| v0.1.6 | In-app branding consistency |
| v0.1.5 | Desktop binary renamed gui → vigils |
The v0.1.7 audit summary is published as a security advisory.
安装 Vigils
English: Install Vigils
1. 我该下载哪个?
| 我想… | 下载 | 跳转 |
|---|---|---|
| 用这个 App —— 看活动、批准操作(图形界面) | 对应系统的桌面安装包 | §2 |
| 守护我的 AI 编码助手 —— Claude Code / Codex / Cursor / Zed | CLI(vigils-cli-…) | §3 |
| 守护往 AI 网站粘贴 —— ChatGPT / Claude / Gemini | 浏览器扩展 | §4 |
大多数人要的是桌面 App;给 agent 接网关的开发者要的是 CLI。
所有文件都在 Releases 页
(把下文的 <version> 换成你下载的版本,例如 0.3.0)。
2. 桌面 App
选你的系统,下载,运行。首次启动会有一次性的“未知开发者“提示(我们暂未做 OS 代码签名)——放行步骤见下。
Windows
- 下载
Vigils_<version>_x64-setup.exe→ 双击。 - 若弹出蓝色 “Windows 已保护你的电脑” → 更多信息 → 仍要运行。
- 完成。(随着安装量增加,该警告会自行消退。)
macOS
- 按芯片下载 dmg —— Apple Silicon:
Vigils_<version>_aarch64.dmg;Intel:Vigils_<version>_x64.dmg(不确定?苹果菜单 → 关于本机 —— 显示“Apple M…“即 Apple Silicon)→ 打开 → 把 Vigils 拖进 应用程序。 - 首次启动会被拦(“Apple 无法验证…”)。一次性放行: ** 系统设置 → 隐私与安全性 → 下拉到底 → 点“仍要打开“ → 用 Touch ID / 密码确认。**
- 完成 —— 之后不再询问。
终端替代方案(进阶用户):
xattr -dr com.apple.quarantine /Applications/Vigils.app这是任何“尚未公证“App 的标准一次性步骤;签名已在计划中。
Linux(Ubuntu 22.04+ / Debian 12+)
sudo dpkg -i Vigils_<version>_amd64.deb # Debian / Ubuntu
sudo rpm -i Vigils-<version>-1.x86_64.rpm # Fedora / RHEL
chmod +x Vigils_<version>_amd64.AppImage && ./Vigils_<version>_amd64.AppImage # 便携版,任意发行版
发行版较老(桌面 GUI 需要 glibc ≥ 2.35)?改用 CLI —— 它几乎能在任何 Linux 上跑(glibc ≥ 2.17)。
3. CLI —— 守护你的编码助手(30 秒)
一行装好:
curl -fsSL https://vigils.ai/install.sh | sh # macOS / Linux
irm https://vigils.ai/install.ps1 | iex # Windows(PowerShell)
(或从 Releases 下载 vigils-cli-<os> 解压 —— 里面有 vigil-hub。)
然后开启防护:
vigil-hub setup # 自动探测 Claude Code / Codex / Cursor 并接好守护
重启你的 agent —— 就这样。明文密钥从此被挡在它的工具调用之外,每次拦截都记进防篡改的本地账本。
想先试试就跑 vigil-hub demo(零设置)。各 agent 细节见 Agent 集成。
ML 变体(可选,语义 PII + 提示注入检测):改下载
vigils-cli-ml-<os>,跑vigil-hub serve --engine ml。 底线:Linux glibc ≥ 2.28、macOS ≥ 14(仅 Apple Silicon —— 上游 onnxruntime 已停发 x86_64 macOS 构建,Intel mac 使用默认硬指纹引擎)。见 隐私过滤器。
4. 浏览器扩展(Chrome)
在 AI 网站上粘贴/提交前先脱敏密钥与 PII。从 Chrome 应用商店一键安装
Vigils Browser Guard ——
普通模式开箱即用,无需桌面 App。可选深度脱敏:安装 Vigils CLI,用扩展 ID 注册 native host
(vigil-native-host install --extension-id <ID>,ID 在扩展「选项 → 高级设置」里可复制),
再到选项页企业模式下选择「本机 Vigils 引擎」——检查将路由到本机引擎(硬指纹 + daemon
运行时的 ML PII 识别),原文不出设备。
开发版:chrome://extensions → 开 开发者模式 → 加载已解压的扩展程序 → 选 extensions/chrome-mv3/。
担心来源真伪? 每个下载都带 SHA-256 校验和 + 密码学构建证明 —— 见 验证你的下载。 想把 Vigils 嵌进自己的 Rust 应用?见 SDK 快速上手。
验证你的下载
🌐 English: Verifying your download
Vigils 守在你的密钥前面,所以你不该仅凭我们一句话就相信某个下载是正版。每一个发布产物 都是可独立验证的。本页说明怎么验证——并诚实交代每种方法能证明什么、不能证明什么。
速览
# 最强校验:证明该文件由「本仓库」的 CI 从「本仓库」的源码构建。
# 适用于全部产物——CLI 压缩包、桌面安装包、扩展 zip。
gh attestation verify <下载的文件> --repo duncatzat/vigils
输出 ✓ 且源仓库 / workflow 匹配,即说明二进制是正版;严格来说无需更多步骤。下面各节是 互补校验,并解释当前为何还没有操作系统级代码签名。
1. 构建溯源(Build provenance)——推荐,覆盖全部产物
每个可下载产物都附带一份 SLSA 构建溯源证明,由 GitHub 背后的 Sigstore 基础设施签发(你无需自管密钥,除 GitHub 与公开源码外无需信任其他方):
- CLI 压缩包——
vigils-cli-linux-x64.tar.gz、vigils-cli-macos-arm64.tar.gz、vigils-cli-macos-x64.tar.gz、vigils-cli-windows-x64.zip - 桌面安装包——
.exe、.msi、.dmg、.deb、.rpm、.AppImage - 浏览器扩展——
vigils-chrome-extension.zip
用 GitHub CLI 验证:
gh attestation verify Vigils_0.1.7_amd64.deb --repo duncatzat/vigils
通过即证明该文件由「本仓库」的发布流水线、从某个具体 commit 构建——也就是说它没有被 掉包、没有被第三方重新构建、构建后也没有被篡改。这对「来源」的证明强于单纯的代码签名 证书,因为它把二进制绑定到了你可以亲自去读的那个公开源码 commit 和 CI 运行。
离线 / 隔离网环境?从 release 下载该产物的 attestation bundle,离线验证:
gh attestation verify <文件> --bundle <bundle.jsonl> --repo duncatzat/vigils。
2. 校验和(Checksum)——CLI 压缩包
每个 CLI 压缩包旁边都发布了 .sha256 文件。一行安装器会自动校验;
手动验证:
# macOS / Linux —— .sha256 内容格式为 "<hash> <文件名>"
shasum -a 256 -c vigils-cli-linux-x64.tar.gz.sha256
# Windows —— 与 vigils-cli-windows-x64.zip.sha256 中发布的 hash 比对
(Get-FileHash -Algorithm SHA256 vigils-cli-windows-x64.zip).Hash
校验和只能证明你下到的字节和 release 发布的字节一致(用于发现截断 / 传输损坏),它
不是签名——认证真伪请用溯源(§1)。桌面安装包依赖溯源,不另发 .sha256。
3. 安装脚本
一行安装命令是一个朴素、可读的 shell / PowerShell 脚本——运行前先读它。注意它会替你 校验压缩包的校验和:
curl -fsSL https://vigils.ai/install.sh # 先读
curl -fsSL https://vigils.ai/install.sh | sh # 再运行
irm https://vigils.ai/install.ps1 # 先读
irm https://vigils.ai/install.ps1 | iex # 再运行
4. 关于「未签名应用」警告
Vigils 安装包尚未做操作系统级代码签名 / 公证——那需要 Apple 与 Windows CA 颁发的、 付费且经身份核验的证书,目前还没有到位。因此首次运行时系统会警告:
- macOS——Gatekeeper 会拦截。先验证溯源(§1),再清除隔离标记:
xattr -d com.apple.quarantine /Applications/Vigils.app。 - Windows——SmartScreen 会提示。先验证溯源(§1),再点 More info → Run anyway。
在代码签名落地之前,构建溯源就是验证路径——而对「证明二进制确实来自这份开源代码」 而言,它本就是更强的保证。
5. 自动更新是签名的
安装后,桌面应用的自动更新器只接受携带项目 minisign 密钥有效签名的更新(每个 release
里的 .sig 文件),因此更新通道无需你任何操作即端到端可信。见
自动更新。
Agent 接入与测试指南
🌐 English: Agent Integration & Test Guide
把 Vigils 插在你的 AI agent 与它调用的工具之间,让 agent 的每一次工具调用都经过 防火墙(默认拒绝)、审计(防篡改哈希链)、脱敏(密钥 / PII),高危调用还会进入 人工审批。全部本地运行,数据不外传。
支持任何兼容 MCP 的 agent:Claude Code、Codex、Cursor、Zed、Kimi CLI、 ZCode、pi、OpenCode、Continue 等。
工作原理
Vigils 作为 MCP 网关运行:你的 agent 通过 stdio 连接 vigil-hub,由 vigil-hub 代理你真正的
MCP 工具服务器(“upstream”),对每次调用进行管控。
┌──────────────────┐ stdio JSON-RPC ┌────────────────────┐ ┌──────────────────┐
│ 你的 agent │◄──────────────────►│ vigil-hub serve │─────►│ 上游 MCP server │
│ Claude Code / │ │ --stdio │ │ (filesystem、 │
│ Codex / Cursor /│ │ ┌──────────────┐ │ │ github、db…) │
│ Zed / ... │ │ │ 防火墙 │ │ └──────────────────┘
└──────────────────┘ │ │ 审计账本 │ │
│ │ 脱敏 │ │
│ │ 审批 │ │
│ └──────────────┘ │
└────────────────────┘
每个 upstream 的工具会用 __(双下划线)分隔符做命名空间化 —— <server>__<tool>,例如
fs__read_file、github__create_issue —— 聚合进 agent 看到的 tools/list。agent 调用某个工具时,Vigils 会在转发之前用防火墙评估它、在审计账本记一条决策,
然后放行、拒绝、或排进审批队列等你确认。
前置条件
安装 CLI 网关 vigil-hub:
- 预编译:从最新 release 下载
vigils-cli-<target>.tar.gz(Windows 为.zip),内含vigil-hub与vigil-native-host。把vigil-hub放进PATH。 - 从源码:
cargo install --path apps/vigil-hub-cli
验证:vigil-hub --help
最快路径 —— Claude Code,一条命令(setup --all)
如果你用 Claude Code,完全不必手写配置。一条命令检测你的环境并全面保护 —— 既有配置会被备份,只新增 Vigils 自己的条目(完全可逆):
vigil-hub setup --all
setup --all 同时接入两层:
- 原生工具输入侧守门 ——
PreToolUsehook,于是每次工具调用(Bash、Edit、Write、Read、MCP 工具……)执行前都先被检查:真实凭据流入工具会被 fail-closed 拦截并审计。Claude Code 与 Codex 还会注册UserPromptSubmithook,贴进对话框的裸凭据在进模型前就被拦下。 - MCP 网关 —— 把你每个 stdio MCP server 改写为经 Vigils 路由,工具结果里的 secret 在模型看到
之前被脱敏,每次调用都被审计。默认 monitor 姿态(你的 server 保持可用;裸 secret 拦截、结果脱敏、
审计照常)。加
--enforce升级 default-deny 硬拦。
vigil-hub setup --mcp --doctor # 接入前预检:每个被包裹的 MCP server 真能启动吗?(只读 PATH 检查)
vigil-hub verify # 用过 agent 后:确认防篡改审计链完整(桌面应用可看拦了什么)
vigil-hub setup --all --uninstall # 移除全部(配置逐字节还原)
重启 Claude Code 即受保护。
setup 自动探测覆盖面(2026-07):
| Agent | Hook(原生工具守门) | MCP wrap(server 网关) | 涉及配置 |
|---|---|---|---|
| Claude Code | ✅ | ✅(user + local scope) | ~/.claude/settings.json + ~/.claude.json |
| Codex CLI | ✅(需在 Codex 内一次性 /hooks 批准 —— 批准前如实显示 pending trust;CI / headless 自动化可用 Codex 0.154+ 的 codex exec --dangerously-bypass-hook-trust,仅限已审核 hook 来源的环境) | ✅(尊重 $CODEX_HOME) | $CODEX_HOME/hooks.json + config.toml |
| Gemini CLI | ✅ | —(MCP 嵌在共享 settings.json,留后续增量) | ~/.gemini/settings.json |
| Cursor | ✅ | ✅ | ~/.cursor/hooks.json + mcp.json |
| Windsurf | — | ✅ | ~/.codeium/windsurf/mcp_config.json |
| Kimi CLI | —(hooks 为 Beta 且官方 fail-open,等 GA 再评) | ✅ | ~/.kimi/mcp.json |
| ZCode | —(仅 plugin 形态 hook) | ✅(--apply 前请先关闭 ZCode) | ~/.zcode/cli/config.json 的 mcp.servers |
| pi | —(无 hook 机制) | ✅(经 pi-mcp-adapter 约定文件 —— pi 本体无内置 MCP) | $PI_AGENT_DIR/mcp.json(默认 ~/.pi/agent/) |
ZCode 说明:工作区级
<项目>/.zcode/config.json是项目内文件(可能被提交),刻意不碰;共享~/.agents/mcp.jsonfallback 亦不改写(多宿主文件,身份归属未定)。用 ZCode 设置面板配置 MCP(默认写回用户级.zcode配置)的用户获得完整保护。pi 的mcp.json不存在即“无可保护 项“——绝不会替你创建。
本指南余下部分是手动路径 —— 用于非 Claude agent(Codex / Cursor /
Zed / …)或你想显式控制 serve 网关及其 upstreams.json 时。
第 1 步 —— 冒烟测试 vigil-hub(30 秒,不用 agent)
接任何 agent 之前,先确认网关能说 MCP。给它喂一个 initialize + tools/list(MCP stdio 是逐行
JSON-RPC):
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| vigil-hub serve --stdio --ledger ./vigil.db
预期 stdout(两条 JSON-RPC 响应):
{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":false}},"protocolVersion":"2025-06-18","serverInfo":{"name":"vigil-hub","version":"0.1.7"}}}
{"id":2,"jsonrpc":"2.0","result":{"tools":[]}}
tools/list 为空是因为还没配 upstream(下一步配)。启动提示走 stderr(stdout 留给协议)。
第 2 步 —— 声明你的工具服务器(upstreams.json)
列出你要 Vigils 代理的 MCP server。裸命令会自动经 PATH 解析。
{
"upstreams": [
{ "name": "fs", "argv": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/data"] },
{ "name": "github", "argv": ["npx", "-y", "@modelcontextprotocol/server-github"] }
]
}
传给 serve:
vigil-hub serve --stdio --ledger ./vigil.db --upstream-config ./upstreams.json
对每个条目,Vigils 会注册该 server、固定其启动命令,并在启动子进程之前做一次
gate-before-spawn 校验(argv + resolved-program 双 drift),然后把它的工具命名空间化
(fs__…、github__…)聚合进 tools/list。
HTTP / 远程 MCP server 改走 OAuth onboarding:
vigil-hub add-remote-mcp --url https://mcp.example.com/ --client-id <id> --scopes mcp:tools.read
第 3 步 —— 把 agent 指向 vigil-hub
用同一个 ledger 路径,让桌面应用和 CLI 看到同一份审计。桌面应用读
data_local_dir()/Vigil/ledger.sqlite3:
- Windows:
%LOCALAPPDATA%\Vigil\ledger.sqlite3 - Linux:
~/.local/share/Vigil/ledger.sqlite3 - macOS:
~/Library/Application Support/Vigil/ledger.sqlite3
文件名必须恰好是 ledger.sqlite3(带 3)、目录是 data_local_dir() —— 路径不一致桌面就读到
另一个文件,Activity Feed 一直空。最省心:跑 vigil-hub setup --mcp / setup --all,自动指向这个
共享账本。手动 serve --stdio 切勿省略 --ledger —— 省略 = 内存账本,桌面看不到。serve/wrap
启动时会打印解析后的账本路径,可与桌面读的路径比对。
下面的片段里,替换 --ledger / --upstream-config 路径和 vigil-hub 路径(Windows 用绝对
.exe 路径,如 C:\\Vigil\\vigil-hub.exe)。
Claude Code
项目根 .mcp.json(或用户级 ~/.claude.json 的 mcpServers):
{
"mcpServers": {
"vigil": {
"command": "vigil-hub",
"args": ["serve", "--stdio", "--ledger", "~/.local/share/Vigil/ledger.sqlite3", "--upstream-config", "./upstreams.json"]
}
}
}
在 Claude Code 里运行 /mcp —— vigil 应显示已连接,其下挂着你的 upstream 工具。
Codex(OpenAI Codex CLI)
~/.codex/config.toml(或项目级 .codex/config.toml):
[mcp_servers.vigil]
command = "vigil-hub"
args = ["serve", "--stdio", "--ledger", "~/.local/share/Vigil/ledger.sqlite3", "--upstream-config", "./upstreams.json"]
Cursor
~/.cursor/mcp.json(或项目级 .cursor/mcp.json):
{ "mcpServers": { "vigil": { "command": "vigil-hub", "args": ["serve", "--stdio", "--upstream-config", "./upstreams.json"] } } }
Zed
~/.config/zed/settings.json:
{ "context_servers": { "vigil": { "command": { "path": "vigil-hub", "args": ["serve", "--stdio", "--upstream-config", "./upstreams.json"] } } } }
OpenCode
项目根 opencode.json:
{ "mcp": { "vigil": { "type": "local", "command": ["vigil-hub", "serve", "--stdio", "--upstream-config", "./upstreams.json"], "enabled": true } } }
Continue(VS Code / JetBrains)
~/.continue/config.yaml:
mcpServers:
- name: vigil
command: vigil-hub
args: ["serve", "--stdio", "--upstream-config", "./upstreams.json"]
第 4 步 —— 验证它真的在管控
等 agent 跑一次工具调用(或你自己触发一次)后,查本地账本。inspect 输出单行 JSON,可接 jq:
vigil-hub inspect --db-path ./vigil.db protection # 一眼看清:裸 secret 拦截、泄漏脱敏、链完整
vigil-hub inspect --db-path ./vigil.db activity --limit 20 # 最近事件 / 决策
vigil-hub inspect --db-path ./vigil.db search "read_file" # 全文搜索审计链
vigil-hub inspect --db-path ./vigil.db approvals list # 待你处理的高危调用
vigil-hub inspect --db-path ./vigil.db verify-chain # 防篡改链校验
# → {"kind":"ChainVerification","data":{"ok":true,"broken_at_event_id":null,"message":null}}
或打开 Vigils 桌面应用实时看:Activity Feed、Approval Queue(批准 / 拒绝)、 Server Registry、Session Replay、Privacy Findings。
“管控“长什么样:默认防火墙是 deny-by-default,一次高危工具调用要么被直接拒绝,要么进 Approval
Queue —— 在你批准之前,agent 的这次调用一直阻塞。决策会记进 activity。
可选 —— 开启 ML 隐私过滤
Vigils 默认用快速硬指纹规则(无 ML)。要加 ONNX PII 扫描器,用 ort feature 编译 CLI 并传
--enable-privacy-filter:
cargo install --path apps/vigil-hub-cli --features ort
vigil-hub serve --stdio --upstream-config ./upstreams.json --enable-privacy-filter
如果传了 flag 但二进制没用 --features ort 编译,启动会 fail-closed(绝不静默地在你以为开了过滤
的情况下不开就跑)。
故障排查
command not found/ agent 起不了 vigil-hub —— 配置里用vigil-hub的绝对路径(Windows 为vigil-hub.exe);vigil-hub --version验证可执行。- 连上了但没工具 —— 你没传
--upstream-config,或文件里没列 upstream。补上upstreams.json。 - 某个 upstream 起不来 —— 确认它的
argv能独立跑,且npx/node(或它需要的东西)在PATH。 - 桌面应用不显示事件 —— 把
--ledger指向桌面应用用的同一路径(第 3 步),且 agent 子进程可写。 - agent 日志里有乱字节 —— stdout 只能有 JSON-RPC;
vigil-hub所有 banner 都走 stderr。
参考
- 架构 · MCP Hub · Action Firewall
apps/vigil-hub-cli/src/serve.rs——serve实现
English version: update-check.md
每日更新检查(version ping)
一句话:
vigil-hub serve/vigil-hub daemon start每天最多向vigils.ai发一次GET,只带平台和本机版本号,用来提醒你有新版本;服务端把这些请求按天计数,这就是 Vigils 唯一的采用度量。没有任何标识,可以一条命令关掉,hook路径永远不发。
为什么有它
- 更新提醒:CLI 没有自动更新,用户需要知道有新版本(尤其是安全修复)。
- 采用度量:Vigils 不做产品遥测(不采集使用行为、不上报事件)。项目要判断该不该继续投入,只需要一个数字——每天有多少安装在跑。把「真实更新检查」按天计数得到的就是 ADI(Active Daily Installs,Firefox 2008–2020 用同样口径),这也是项目 FAQ 早已对外承诺的唯一出站流量。
- 这不是「遥测 opt-in」原则的例外:那条原则管的是崩溃上报(带堆栈),从未实装;更新检查是另一件事。
到底发了什么
每次就是下面这一条请求,没有别的:
GET /desktop-updates/windows-x86_64/0.7.1.json HTTP/1.1
Host: vigils.ai
User-Agent: vigil-hub/0.7.1
- 路径里的
windows-x86_64= 平台(darwin-aarch64/linux-x86_64…),0.7.1= 本机 vigil-hub 版本。 User-Agent= 产品名 + 版本号(桌面版另用vigils-desktop/<版本>)。- 没有:设备 ID、随机 ID、周桶、盐、机器名、用户名、路径、账本内容、任何配置、任何 query 参数、任何 body。
- 不跟随跳转;清单最大读 64 KB;响应只取
version字段并按 SemVer 子集校验后才会展示。
集成测试 apps/vigil-hub-cli/tests/update_check.rs 用本地一次性 HTTP 服务器抓包断言以上每一条(只发一次、只这两项、无多余请求头、三种关闭方式零出站)。
什么时候发
| 入口 | 行为 |
|---|---|
vigil-hub serve --stdio | 启动时判断,后台线程发;stdout 一个字节不碰(那是 MCP 协议通道),只在 stderr 打行 |
vigil-hub daemon start | 同上,在模型暖载之后 |
vigil-hub hook(每次工具调用) | 永不发。短进程 + 延迟预算;tests/update_check.rs 用源码守门断言 hook.rs / command_guard.rs / posture.rs 不引用更新检查 |
| 其它子命令 | 不发 |
- 每 24 小时最多一次(按安装计):本地只落一个节流文件
<data_local>/Vigil/update-check.last(内容 = 上次尝试的 Unix 秒)。这是本功能唯一的本地落盘,而且先记时间再发请求——离线 / 失败也不会在 24h 内重发。 - 事前告知:
vigil-hub setup(apply 类操作)动手前在 stderr 打一行说明;serve/daemon第一次尝试前也打一行。之后不再重复。 - 拿不到本地数据目录(无法节流)→ 不发。宁可少测,不多发。
有新版本时 stderr 会多一行:
vigil-hub 0.7.1: a newer release 0.8.0 is available -> https://github.com/duncatzat/vigils/releases/latest
怎么关
三种方式任一即生效,且优先于一切:
| 方式 | 说明 |
|---|---|
vigil-hub version-ping off | 写入 <data_local>/Vigil/version-ping.off;on 删除它 |
VIGIL_NO_VERSION_PING=1 | 产品专属环境变量(任何非 0 / false / off / no 的非空值) |
DO_NOT_TRACK=1 | 行业通用约定(https://consoledonottrack.com) |
手动检查一次并看清结果(含失败原因;关闭状态下拒绝执行,不例外):vigil-hub version-ping check。
查看状态:
$ vigil-hub version-ping status
version ping: enabled
what: one GET per day from `serve` / `daemon start`, to notice a newer release
sends: platform + version only, no identifiers
request: GET https://vigils.ai/desktop-updates/windows-x86_64/0.7.1.json
last try: 3h ago
turn off: vigil-hub version-ping off (or VIGIL_NO_VERSION_PING=1 / DO_NOT_TRACK=1)
vigil-hub version-ping status --json 输出稳定 schema(与界面语言无关):enabled / disabled_by(env:VIGIL_NO_VERSION_PING / env:DO_NOT_TRACK / marker / no-state-dir / null)/ endpoint / user_agent / sends / last_attempt_unix / min_interval_secs。
自托管或内网镜像:VIGIL_UPDATE_ENDPOINT=https://mirror.example.com(基址;路径规则不变)。
服务端留什么
- 源站是
vigils.ai(nginx,Cloudflare 前置)。/desktop-updates/*是no-cache,每条请求都到源站。 - nginx 用默认
combined访问日志:时间、请求行、状态码、UA,以及连接方 IP——经 Cloudflare 代理后这是 Cloudflare 边缘节点的 IP,不是你的 IP(源站没有开启 real-IP 还原,也不记录CF-Connecting-IP)。日志按天轮转,保留 14 天。 - 聚合脚本(
/opt/vigils/ota-adoption.py,每日一次)只输出计数:按(日, 平台, 版本)的请求数、7 天 ADI 均值、平台 / 版本分布。落盘的报告与历史里没有 IP、没有 UA 原文。 - 聚合结果计划公开在
https://vigils.ai/stats/adoption.json(与 Homebrew / Fedora 的做法一致:被计数的人能看到数字)。
法务口径(GDPR / ePrivacy)
- 客户端只落一个节流时间戳,它是「更新检查」这个用户可感功能自身的严格必要存储;不落盐、ID、桶(EDPB Guidelines 2/2023 v2.0 把写入终端设备的任何信息都纳入 ePrivacy 5(3),所以设计上把「存储」收窄到这一个文件)。
- 服务端瞬时看到的连接 IP 走 GDPR 第 6 条 (1)(f) 正当利益;正当利益评估(LIA)摘要:目的 = 更新提醒与安装计数;必要性 = 已是最小数据(平台 + 版本);权衡 = 无标识、可关闭、事前告知、聚合结果公开、日志 14 天即删。
- 对外措辞统一为:「无使用行为遥测;仅一个可关闭的计数式更新检查」,不再写「零网络请求」之类的简写。
版本与范围
- 引入版本:下一个发布版(在前一版 release notes 预告后才启用)。
- 本轮只接 CLI(
serve/daemon);桌面版在 Settings 加开关后再接(桌面 UA 为vigils-desktop/<版本>)。 - 相关代码:策略层
crates/vigil-update-check(纯逻辑,无网络,可离线测试);接线apps/vigil-hub-cli/src/update_check.rs;测试apps/vigil-hub-cli/tests/update_check.rs。