Releases: vkmtx/veil-mcp
Release list
v0.7.1 — correctness + security follow-up
A correctness + security follow-up from a full read-only audit. Smoke coverage grew
from 371 to 429 assertions; the stress harness now fails on an anomaly (was report-only),
and typecheck now also covers test/ and bench/.
Security
- Record-store path traversal closed.
sh_detail/sh_logsreject any id that isn't a
well-formedcmdNbefore it reaches the filesystem, so a crafted id can no longer read a
JSON file outside the per-project store. - Checkpoints are private and transactional. The per-project checkpoint store is now
owner-only (0700, like the record store), and a snapshot is published atomically (built
in a staging sibling, then renamed over the target) so a crash mid-copy can never leave a
partial tree that a later restore would apply. sandbox { network: false }(Linux/bubblewrap) also masks Unix sockets.--unshare-net
isolates TCP/UDP but left/run/docker.sockreachable;/runand/var/runare now
overlaid with tmpfs so a confined command can't reach the host Docker/Podman daemon.
Fixed
background: truewith an invalid cwd no longer crashes the server — the spawn error
listener is wired before the early return, so the async failure is absorbed.sh_logsno longer duplicates output after a background run exits. The durable record
keeps per-stream byte totals so a poll after the live→durable handoff returns only new
output; slices honor the cursor for binary too and never split a UTF-8 codepoint.sh_killreportsterminating(signal sent) instead of claiming the process is dead
with exit 137 before it actually closes.- Children get
/dev/nullstdin — a command that reads stdin gets EOF instead of hanging
to the timeout. files_changedcatches a re-modified already-dirty file (its git status line unchanged),
which also fixesexpect.changedfor that case.- Effects honesty: a syscall trace is bounded by
VEIL_MAX_STREAM_BYTES(with
trace_truncated) and no longer counts a failedopenas a write; a largepreviewdiff
reportspreview_effects_incompleteinstead of collapsing to "nothing changed". BoundedBuffercaps a single chunk larger than the byte cap.- Classifier: a raw newline is now a command separator, and
git config <set>,
branch/tag creation, andgit reflog expire/deleteclassify as writes (were read-only). - ReDoS guard:
stdout_matchesandsh_detail matchrefuse catastrophic-backtracking
patterns instead of running them on the single-threaded event loop.
Changed
veil-guard.shanchors the delete rule to executable position —echo rm -rf xand
grep "rm -rf" fno longer false-block, whilerm -rf,sudo rm -rf, wrapper forms
(timeout/nohup/nice/…), and operator/{/dopositions still block.- Release workflow fails loud (no more silent skip), runs the full gate with
npm ci,
verifies the tag matchespackage.json/package-lock.json, smoke-tests the packed
tarball, and publishes with npm provenance. - Node engines bumped to
>=22(matches CI).
v0.7.0
Hardening + capability backlog (16 issues). Background processes (sh_run background + sh_logs + sh_kill + shutdown reaping), env-secret scrub + no_store, complete trace effects, per-repo effect-diff serialization, per-project checkpoints, classifier over-flag tuning, --version/--help. See CHANGELOG.md for full notes.
v0.6.0
[0.6.0] — 2026-06-23
Four scoped capabilities, each shipping only what its substrate can honestly back
(no headline that the kernel or the stored data can't keep), plus a guard-hook
refresh and an honesty/correctness audit pass.
Added
- Secret read-confine.
sandboxgains{ protect_secrets: true }(built-in
credential-dir denylist:~/.ssh,~/.aws,~/.gnupg,~/.config/gcloud|gh,
~/.kube,~/.docker) and{ deny_read: [...] }(extra dirs). macOS appends
(deny file-read* (subpath …))to the SBPL profile (rule order: later wins, so it
overridesallow default); Linux masks each dir with an empty--tmpfs. Reports
secrets_protected: <n>. Scoped by design — it blocks the listed paths, not a
proof against all exfiltration. - Dry-run preview.
sh_run { preview: true }runs the command inside a
disposable CoW clone of cwd, returns the cwd-relativefiles_changed, and never
touches the real cwd (nothing is promoted). Refuses if the clone can't be made.
Honest scope, bannered inpreview_warning: absolute-path / parent-dir / network
effects are not captured and may happen for real — this is not a sandbox. sh_history. Descriptive aggregates over past runs of a command — observed
exit / retry-recovery / durationp50/p90/ file-churn with explicit sample size
nand recency window. Restates the local (capped, TTL-pruned) store; makes no
prediction and no causal claim. Read-only. (RunRecordgains anattimestamp.)- Landlock Linux sandbox backend (K++). A namespace-free fallback (via
landrun,
kernel 5.13+) that write-confines in containers / CI / Ubuntu 24.04+ where
bubblewrap's unprivileged user namespaces are restricted.sandboxAvailable()now
reports true on Linux with bwrap OR Landlock;wrapCommandprefers bwrap and
falls back to landrun. Scoped, honest: write-confine only — it refuses (so the
caller refuses) network-deny / secret-read-confine rather than fake them, and its
self-test runs a real confined no-op (no--best-effort) so an unsupported kernel
reports unavailable instead of silently running free. - Guard hook covers modern verbose tools.
hooks/veil-guard.shnow routes
bun/deno/uv(install/add/run/test/build/sync) and image builds
(docker build/buildx/compose build/docker-compose build) tosh_run, while
read-only (docker ps/logs) and long-running (bun run dev,docker compose up,
* --watch) forms still pass through to raw Bash. Danger branch, fail-open, and
VEIL_BYPASS=1are unchanged.
Fixed (audit pass)
expectcontent checks no longer lie on binary/truncated output.
stdout_contains/stdout_matchesdecode a base64 (NUL-byte) stream before testing,
and a non-match on a byte-cap-truncated stream is annotated inconclusive (the
needle may be in the dropped head) instead of a confidentpass:false.effectsFromTracecanonicalizes cwd — a symlinked root (/tmp→/private/tmp,
symlinked$HOME) no longer silently drops real in-cwd writes fromfiles_changed.- Read-confine discloses what it can't protect. A requested secret that exists as a
FILE (the dir-only backend can't mask it) is surfaced insecrets_unprotectedrather
than silently dropped whilesecrets_protectedcounts only the dirs. - Server version is read from package.json (was hardcoded
0.4.0, drifting a full
minor behind), asserted equal in the smoke suite. - Landlock knob-refusal is machine-readable (
sandbox_unsupported_feature: true). - Eviction has a memory backstop (records that never reached a flaky disk can't grow
unbounded);nextIdwon't hand back an id whose record file already exists; numeric
env tunables are floored at 0; a committed-then-clean file is labelled "no longer
dirty (committed or reverted)" instead of asserting "(reverted)"; the preview banner
notes.gitchanges are excluded from its diff. Removed deadsandboxSelfTest.
v0.5.0 — security + honesty hardening
Skeptic-review hardening pass — 10 merged PRs. Reproduce every number with npm run metrics.
🔴 Security
- Checkpoint label
..no longer wipes the temp dir —safeLabeladmitted./..; a..label madecheckpoint()resolve to the temp root andrmSyncit. Rejected +containedPath()containment at every join site. git clean --forceclassified destructive (was read-only → no effect-tracking / nudge); a glob/redirect/$()no longer downgradesrm */shred f > /dev/null/git reset --hard $(…)tocomplex.- Record store is owner-only (
0700dir /0600files) — captured stdout/stderr no longer world-readable on shared hosts. - Guard hook hardened —
VEIL_BYPASSonly as a leading env-assignment (no comment bypass); danger set coversfind -delete,git clean -f/--force,chmod -R,shred,truncate,rm --recursive/--force; verb patterns anchored so benign arguments aren't mis-blocked.
Fixed
veil initnever deletes user content between mismatched markers.- Condensing surfaces more signal — the lexicon covers crash idioms (
SIGSEGV,CONFLICT,! [rejected],timed out, …) plus a+N moreoverflow note instead of a silent cap-at-5. - Snapshot honesty —
method: "clone"is reported only within one volume; cross-volume / non-APFS falls back torsync. - Falsifiable backtest floor — a per-short-command envelope-overhead check the byte-weighted net% structurally could not catch.
Added
npm run metrics— agent-turns-saved (55% fewer round-trips), sandbox-escapes-blocked (5/5), signal-recall (100% on a labeled corpus), checkpoint cost (CoW clone vs rsync) — deterministic rows asserted in the suite so the figures can't drift.- "Why veil" + Metrics README sections; the over-claims a review flagged corrected to match what ships (lazy sandbox probe, guard = routing not containment, byte- not token-savings, 228 assertions).
Full detail: CHANGELOG.
veil-mcp v0.4.0
veil-mcp v0.4 — hardening release
A pass over six external critiques, with nine additional issues fixed from an
adversarial multi-agent review (two were genuine classifier under-flagging — the
unsafe direction).
Highlights
sh_detailsurvives a server restart. New disk-backed record store: records
persist to a per-project dir under the OS state location, capped + TTL-pruned,
with atomic id reservation and atomic writes. Best-effort — degrades to
memory-only on a read-only FS, never fails a run.veil init— one command to drop the "prefersh_run" nudge into a project's
CLAUDE.md(idempotent). Closes the setup-friction gap.sh_planis segment-aware —cat f | grep xis read-only,cd b && rm fis
destructive, instead of an opaquecomplex. Relabeled honestly as a static safety
pre-check, not an execution dry-run.
Safety fixes
find -exec shred/-execdir rm/-exec /bin/rm/-exec git reset --hardnow
classify as destructive (were read-only).- Quoted subcommands classify like the bare form (
git "reset" --hardis destructive). - Backslash-escaped operators stay literal.
Docs
Value proposition repositioned (structure + safety first, token economy as a
consequence); sandbox framed as opt-in best-effort (unavailable in containers, by
design); a "verify it yourself" section with reproducible steps.
Install
claude mcp add veil -- npx -y github:vkmtx/veil-mcp
npx -y github:vkmtx/veil-mcp initValidation
typecheck · 196 smoke assertions · backtest 89.7% · 86 stress probes, 0 anomalies —
green on macOS and Linux (CI matrix).
Full changelog: CHANGELOG.md
veil-mcp v0.3.0
veil is an agent-native shell, as an MCP server — the first shell layer designed for an LLM agent (Claude Code), not a human at a terminal. Effects come back as data; detail is addressable and pulled on demand.
Highlights
- Quiet, structured
sh_run— exit / duration / files-changed + token-aware output. ~91% fewer bytes ingested on a realistic command mix; verbose builds/installs 92–98%. - Addressable detail (
sh_detail, incl.match=<regex>grep) — never re-run a command to see its output. - Effects-as-data (git porcelain or syscall trace), post-condition
expect, declarative retry, dry-run blast-radius classification (sh_plan). - Checkpoint/restore with APFS copy-on-write clone.
- Real opt-in sandbox (macOS
sandbox-exec; Linux bubblewrap) and structured trace (Linuxstrace). - Output honesty — mid-stream failures surfaced, truncation labeled, true line counts, binary preserved (never silently lossy).
- Optional PreToolUse guard hook to steer Bash →
sh_run.
Install
claude mcp add veil -- npx -y github:vkmtx/veil-mcp
Quality
173 smoke assertions + an 81-probe stress battery (0 anomalies) + 119-scenario classify fuzz + a token-savings backtest + a 5-dimension benchmark. CI runs on macOS and Linux.
See CHANGELOG.md.