Skip to content

Releases: vkmtx/veil-mcp

v0.7.1 — correctness + security follow-up

Choose a tag to compare

@vkmtx vkmtx released this 21 Jul 17:44

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_logs reject any id that isn't a
    well-formed cmdN before 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.sock reachable; /run and /var/run are now
    overlaid with tmpfs so a confined command can't reach the host Docker/Podman daemon.

Fixed

  • background: true with 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_logs no 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_kill reports terminating (signal sent) instead of claiming the process is dead
    with exit 137 before it actually closes.
  • Children get /dev/null stdin — a command that reads stdin gets EOF instead of hanging
    to the timeout.
  • files_changed catches a re-modified already-dirty file (its git status line unchanged),
    which also fixes expect.changed for that case.
  • Effects honesty: a syscall trace is bounded by VEIL_MAX_STREAM_BYTES (with
    trace_truncated) and no longer counts a failed open as a write; a large preview diff
    reports preview_effects_incomplete instead of collapsing to "nothing changed".
  • BoundedBuffer caps a single chunk larger than the byte cap.
  • Classifier: a raw newline is now a command separator, and git config <set>,
    branch/tag creation, and git reflog expire/delete classify as writes (were read-only).
  • ReDoS guard: stdout_matches and sh_detail match refuse catastrophic-backtracking
    patterns instead of running them on the single-threaded event loop.

Changed

  • veil-guard.sh anchors the delete rule to executable position — echo rm -rf x and
    grep "rm -rf" f no longer false-block, while rm -rf, sudo rm -rf, wrapper forms
    (timeout/nohup/nice/…), and operator/{/do positions still block.
  • Release workflow fails loud (no more silent skip), runs the full gate with npm ci,
    verifies the tag matches package.json/package-lock.json, smoke-tests the packed
    tarball, and publishes with npm provenance.
  • Node engines bumped to >=22 (matches CI).

v0.7.0

Choose a tag to compare

@vkmtx vkmtx released this 25 Jun 19:01

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

Choose a tag to compare

@vkmtx vkmtx released this 23 Jun 08:08

[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. sandbox gains { 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
    overrides allow 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-relative files_changed, and never
    touches the real cwd (nothing is promoted). Refuses if the clone can't be made.
    Honest scope, bannered in preview_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 / duration p50/p90 / file-churn with explicit sample size
    n and recency window. Restates the local (capped, TTL-pruned) store; makes no
    prediction and no causal claim. Read-only. (RunRecord gains an at timestamp.)
  • 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; wrapCommand prefers 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.sh now routes
    bun/deno/uv (install/add/run/test/build/sync) and image builds
    (docker build/buildx/compose build/docker-compose build) to sh_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=1 are unchanged.

Fixed (audit pass)

  • expect content checks no longer lie on binary/truncated output.
    stdout_contains/stdout_matches decode 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 confident pass:false.
  • effectsFromTrace canonicalizes cwd — a symlinked root (/tmp/private/tmp,
    symlinked $HOME) no longer silently drops real in-cwd writes from files_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 in secrets_unprotected rather
    than silently dropped while secrets_protected counts 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); nextId won'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 .git changes are excluded from its diff. Removed dead sandboxSelfTest.

v0.5.0 — security + honesty hardening

Choose a tag to compare

@vkmtx vkmtx released this 23 Jun 03:00

Skeptic-review hardening pass — 10 merged PRs. Reproduce every number with npm run metrics.

🔴 Security

  • Checkpoint label .. no longer wipes the temp dirsafeLabel admitted ./..; a .. label made checkpoint() resolve to the temp root and rmSync it. Rejected + containedPath() containment at every join site.
  • git clean --force classified destructive (was read-only → no effect-tracking / nudge); a glob/redirect/$() no longer downgrades rm * / shred f > /dev/null / git reset --hard $(…) to complex.
  • Record store is owner-only (0700 dir / 0600 files) — captured stdout/stderr no longer world-readable on shared hosts.
  • Guard hook hardenedVEIL_BYPASS only as a leading env-assignment (no comment bypass); danger set covers find -delete, git clean -f/--force, chmod -R, shred, truncate, rm --recursive/--force; verb patterns anchored so benign arguments aren't mis-blocked.

Fixed

  • veil init never deletes user content between mismatched markers.
  • Condensing surfaces more signal — the lexicon covers crash idioms (SIGSEGV, CONFLICT, ! [rejected], timed out, …) plus a +N more overflow note instead of a silent cap-at-5.
  • Snapshot honestymethod: "clone" is reported only within one volume; cross-volume / non-APFS falls back to rsync.
  • 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

Choose a tag to compare

@vkmtx vkmtx released this 22 Jun 20:18

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_detail survives 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 "prefer sh_run" nudge into a project's
    CLAUDE.md (idempotent). Closes the setup-friction gap.
  • sh_plan is segment-awarecat f | grep x is read-only, cd b && rm f is
    destructive, instead of an opaque complex. 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 --hard now
    classify as destructive (were read-only).
  • Quoted subcommands classify like the bare form (git "reset" --hard is 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 init

Validation

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

Choose a tag to compare

@vkmtx vkmtx released this 22 Jun 19:11

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 (Linux strace).
  • 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.