Hardening, supply-chain, and fleet-operations release: fleet-wide egress auditing, a preventive
egress byte cap, pinned/verified replay, and a broad sweep of fail-closed and portability fixes.
All local, self-hosted, no telemetry.
Added
- Fleet-wide egress audit —
sluice egress --verify --all/--export --allwalk every box's append-only audit log and hash chain with the engine down (no daemon needed). (#67) SLUICE_EGRESS_HARD_CAP_BYTES=N— a preventive in-box egress cap (viaxt_quota) that kills an over-cap transfer mid-flight; fails closed when the kernel module is absent. (#67)- Per-host egress budget (
SLUICE_EGRESS_MAX_BYTES) metered on what left the box; the direct-IPSLUICE_ALLOW_IPSlane is now metered before the ESTABLISHED accept. (#67) sluice lock --pinwrites a committablesluice.pinreplay manifest (base pinned by digest + exact versions);SLUICE_PIN=1performs a verified pinned replay build. (#67)- Versioned JSON contracts (
sluice.version/doctor/boxv1);ls --jsonembedslast_receipt; in-box DNS-tunnel audit and bump upload controls. (#67) sluice egressgains a summary header ("N reached, N blocked, <bytes>"), an audit-log footer nudging--export/--verify, andegress --verify --jsonfor CI integrity gating;egress --jsongains"window":"boot". (#43)- Denied raw-IP (direct CONNECT/URL) requests are now ledgered in the receipt and
egress --json, alongside an always-onfw_droppedtotal. (#72) - New managed-policy directives:
max-allow-ips-bytes,max-hard-cap-bytes,strict-unknown,require-signed-base. (#76, #81) sluice doctorreadout: trailing verdict line, always-on hardening row, git-tracked-mask warning, report-only managed-policy preflight (doctor --jsonaddspolicy.effective_allowlist+policy.refusals),SLUICE_MOUNTSvalidation, and rootless-podman host-limit checks. (#48, #46, #45, #72, #71)NOTICEfile + README Acknowledgments crediting the upstream base image, its packages, and the test framework. (#72)- Four capability demo GIFs across the README and docs: a malicious npm dependency contained at import, an exfil killed mid-flight by the hard cap, a fleet audit with the daemon down, and an org policy refusing a metadata-SSRF config.
Changed
- The credential prelaunch hook now runs before every session (run / shell / rebuild, warm boxes included) so short-lived tokens re-mint each run instead of expiring with the container. (#72)
sluice lsALLOW count reads the live allowlist instead of the stale build-time label. (#72)- A config-less first run now stops after scaffolding instead of building + running the detected command under
SLUICE_YES=1. (#72) sluice diffis now gated by the managed policy and config validators like every other box bring-up path. (#76)sluice learndrops a laundering host underforbid-laundering/SLUICE_STRICT_LAUNDERINGinstead of warn-and-keeping it onto the hot-reloaded allowlist. (#76)SLUICE_ALLOW_IPSrejects a hostname entry with a clear message (hostnames belong inSLUICE_ALLOW_DOMAINS) instead of silently sinking it to the DNS dead-end. (#79)sluice lock --scanexit code is a documented contract (0clean /3gate-tripped /4scanner-failed), normalized across grype and trivy;--check/--diff/--enforcereject unknown flags. (#44)- The SBOM (
lock --sbom/--pin) enumerates the full global-npm dependency closure (was top-level only), so a CVE in a transitive npm dep now reaches--scan. (#77) - A signed base image is resolved to a content digest once;
cosign verifyand the DockerfileFROMpin the same digest (closes a same-tag-republish gap on the:latestsigned-base path). (#77) -b <box> doctorresolves the engine leniently, reporting instead of dying on a badSLUICE_RUNTIME. (#55)- Agent presets:
geminidrops the inertGOOGLE_API_KEY,qwenforwardsDASHSCOPE_API_KEY, and stale/dead allowlist hosts were pruned fromclaude/amp. (#55, #65, #49)
Fixed
- Portability (build-breaking): hashing routes through
_sha256(preferssha256sum, falls back toshasum -a 256), unbricking every build/run on Alpine,*-slim, and most CI images whereshasumis absent. (#78) - Data-loss:
sluice apply/diffexclude masked paths from the overlay write-back, so a masked.envno longer truncates the real host secret to 0 bytes. (#75) - Denied raw-IP requests are matched with an interval-free regex, fixing silent under-reporting on
mawkbuilds without--re-interval(e.g. Debian). (#73) - Rootless podman keeps host repo ownership via
--userns=keep-id; podman'slocalhost/image-name prefix is stripped sols/ps/prune/receipt slugs resolve. (#69, #71) - Fail-closed audit reads:
ls --egress,learn, anddoctorreport unknown (?/null) instead of a false all-clear when the in-box audit log can't be read. (#61, #59) set -faround unquoted config word-splits in the launcher and the in-container entrypoint, so a glob char in aSLUICE_*value can't pathname-expand; wildcard allowlist entries stay literal during policy evaluation. (#54, #52, #45)sluice learnno longer aborts when the run-offset file is missing — it falls back to the full log. (#53)SLUICE_SECCOMP=auditwrites its generated profile to the stable per-box state dir instead of leaking one/tmpfile per run. (#54)- The overlay-workspace seed writes its apply-safety manifest only on a fully successful copy, so a partial seed can't delete untouched host files on
apply. (#54) egress --verifyhandles an unterminated final record and skips blank lines (full hash-chain coverage) and rejects typo'd flags. (#42, #55)install.shre-pointsorigintoSLUICE_REPOon a re-run and quotes a spaced$HOMEfpath dir. (#55)lock:LC_ALL=Csorts kill phantom cross-locale drift; same-version rebuilds and multi-version packages classify correctly;--sbom/--jsonstdout is byte-clean on a cold build. (#44, #42)
Security
- Closed egress allowlist fail-opens: the leading-dot wildcard form (
.storage.googleapis.com) thatsluice learnwrites now matchesforbid-laundering/ strict-laundering, andSLUICE_ALLOW_IPSrefuses masks broader than/8(was only/0). (#51) sluice initsingle-quotes every repo-derived value written into the generated config, closing an RCE via a hostile Procfile / make-target /package.jsonscript. (#50)- Closed five managed-policy trust-boundary bypasses:
deny-iprefuses anSLUICE_ALLOW_IPSentry that overlaps the denied CIDR in either direction (supernet included, e.g. a/24over a/32metadata deny); a malformed ceiling arg is a hard refusal, not a fail-open no-op. (#76) sluice learnfails closed on an unreachable managed policy, aligning it with the run path.
Internal
CI / supply-chain hardening (advisory actionlint / zizmor / gitleaks lanes, checksum-pinned grype + container-structure-test installs, SHA-pinned action bumps), a lane-membership drop-guard and a preflight ship-readiness workflow, added regression coverage, docs-drift fixes, and a consolidated Terraform Linux test-runner VM (sluice-vm.sh, Kata opt-in). (#57, #60, #62, #63, #64, #65, #66, #68, #70, #74, #80, #81)
Install
brew install Pyronewbic/tap/sluice # or: brew upgrade sluice
# or: curl -fsSL https://raw.githubusercontent.com/Pyronewbic/Sluice/main/install.sh | shFull changelog: v0.9.0...v0.10.0