Skip to content

Repository files navigation

Cascade

Feed it inputs, then watch the failure cascade — safely contained.

Cascade analyses a binary you didn't build and don't have the source for: it detects what kind of binary it is, boots it in a contained environment (a sandboxed process, or a real microVM for Linux targets), and drives it with an LLM-backed agent that classifies it, load-tests it, and attacks it until it has an evidence-backed report of what the binary does and where it breaks. It is a sibling to Scalemap in the Phase family of tools — same philosophy (understand a system by safely stress-testing it until it breaks), applied to something you found rather than something you designed. The full product definition is docs/superpowers/specs/2026-07-29-cascade-prd.md.

What's in this repository

Two sandbox substrates, chosen automatically per target:

Substrate Target How it's contained
macseatbelt Mach-O binaries (macOS) A macOS Seatbelt (sandbox-exec) profile confining the target directly on the host — no VM, no code-signing, zero setup. Real confinement, not a VM-grade boundary; see the package doc for exactly what that does and doesn't mean.
applevz ELF binaries (Linux) A real microVM via Apple's Virtualization.framework — the target runs inside a from-source-built Linux guest kernel with no network device at all.

An LLM agent (any OpenAI-Chat-Completions-compatible endpoint — OpenAI itself, a local model server, anything speaking the same API) drives three phases against whichever sandbox a target gets: classify (what is this binary, how do I talk to it), load-test, and pentest. It uses one tool, run_in_sandbox, to touch the target, and can escalate the sandbox's resource tier mid-run if it hits a real memory-exhaustion signal.

You don't pick any of this — target format, sandbox substrate, and starting resource tier are all detected from the target file itself. See docs/superpowers/specs/2026-08-02-hands-off-target-provisioning-design.md for the design.

Two ways to drive it: a one-shot CLI (cascade run) and a local web UI (cascade serve) with drag-and-drop upload, a live event stream per run, and a settings screen for the API key.

Package What it is
sandbox The SandboxBackend interface both substrates implement, ResourceLimits, the small/medium/large resource-tier ladder, and EscalatingBackend (reboots a run at a bigger tier on a resource-exhaustion signal).
broker The host↔guest wire protocol (used by applevz) and the guest-side server that dispatches a request to a subprocess.
cmd/broker That server as a standalone linux/arm64 binary — PID 1 inside the guest, listening on vsock.
guest Build tooling for the guest kernel + initramfs applevz boots, plus the release-only embedding step that bundles a built image into the cascade binary.
applevz / macseatbelt The two SandboxBackend implementations.
agent The ReAct tool-calling loop over an OpenAI-compatible chat-completions client, and the one tool (run_in_sandbox) it exposes to the model.
classify / loadtest / pentest The three agentic phases, each an LLM-driven handler in the phase graph.
controller / engine Drives the phase graph (classify → load-test → pentest) and dispatches each phase to its handler.
findings The canonical finding schema every phase reports through.
observe The one event schema + observer interface every frontend (CLI, web SSE) renders from.
runstore Persists run metadata and the event log; serves the web UI's SSE stream.
budget / experiment Wall-clock run budget, category-fallback retries, and the snapshot-fork experiment runner used internally by the phase handlers.
config Resolves the API key / base URL / model from env vars, a config file, or the settings UI.
webserver / web The HTTP API + embedded static assets, and the React frontend source.
cmd/cascade The CLI: smoketest, run, serve.

Prerequisites

  • macOS 14+ on Apple Silicon (arm64) for the applevz (ELF/Linux) substrate — Code-Hex/vz's save/restore APIs compile only under darwin && arm64 and need macOS 14. macseatbelt (Mach-O) has no such requirement beyond a working sandbox-exec, which ships with macOS.
  • Xcode Command Line Tools (xcode-select --install) — applevz is a cgo binding.
  • Go 1.25+ (go.mod pins the floor).
  • An OpenAI-compatible LLM endpoint and API key. Scanning is agentic-only — there is no offline/no-key mode. OpenAI itself works out of the box; anything else speaking the same Chat Completions API (a local model server, a different provider) works by pointing -base-url/CASCADE_BASE_URL at it.
  • Docker, colima, or OrbStack, arm64-container-capable — only if you intend to scan ELF/Linux binaries locally (see "Scanning ELF/Linux binaries" below). Not needed for Mach-O scanning or for building/running the tool at all.

Build

go build -o cascade ./cmd/cascade
# The `ld: warning: ignoring duplicate libraries: '-lobjc'` here is expected.

That's the whole build — the web frontend's compiled assets (webserver/frontend/dist/) are committed and embedded via //go:embed, so nothing else needs building to run cascade serve.

If you're changing the frontend (web/src/**), rebuild and commit the embedded output before your change takes effect in the binary:

cd web && npm install && npm run build

Configure an API key

Any of these, in precedence order:

export CASCADE_API_KEY=sk-...
export CASCADE_MODEL=gpt-4o          # or whatever your endpoint serves
export CASCADE_BASE_URL=https://api.openai.com/v1   # default; only needed for a non-OpenAI endpoint

...or configure the same three via cascade serve's settings screen (gear icon) once it's running — persisted to a local config file, no env vars needed. cascade run without a key configured fails fast with a message pointing here.

Quickstart — web UI (recommended)

./cascade serve

Open http://127.0.0.1:8420. If you haven't set the env vars above, open Settings and enter your API key/model first — the scan form is disabled without one configured.

Drag a binary onto the drop zone (or paste a path already on the machine running cascade serve), set a run budget if you want something other than the default, and click Start Scan. That's the whole form — target format, sandbox substrate, and resource limits are all detected automatically; there's nothing else to configure per scan. Watch the run's live event stream (phase transitions, tool calls, agent reasoning, findings) and its report as it completes.

A Mach-O binary (anything built for macOS) is the easiest thing to try first — no VM, no code-signing, no guest image needed, it Just Works from a plain build. A plain build does mean a smaller macseatbelt analysis-tool set than the substrate can actually offer, though — see "Vendoring extra macseatbelt analysis tools" below. See further below for ELF/Linux targets.

Quickstart — CLI

./cascade run -target /path/to/some/macho-binary

-target-format/-isolation are optional overrides (auto-detected from the target's magic bytes otherwise); -data-dir controls where run records/events are persisted (defaults under your OS config dir). Every run is agentic — there's no separate "demo" mode.

./cascade run -h   # full flag list

Scanning ELF/Linux binaries

ELF targets need a guest kernel + initramfs (the applevz substrate boots a real VM). A release build of cascade ships with one bundled; a locally-built cascade does notgo build always compiles cleanly (the embed target is a small git-tracked placeholder, not the real, gitignored build output), but a locally-built binary has no guest image until you either build one or point at one explicitly:

cd guest && make all   # from-source Linux kernel build inside an arm64 container: several
                        # minutes, one-time. Produces guest/Image + guest/initramfs.cpio.gz
                        # (gitignored — real build output, not something to commit).

Then, for cascade run (which still accepts -kernel/-initramfs as overrides on top of auto-detection):

./cascade run -target /path/to/some/linux-elf-binary \
  -kernel guest/Image -initramfs guest/initramfs.cpio.gz

cascade serve has no per-request override flags — every run through the web UI uses whichever guest image is embedded in the binary. To make a locally-built cascade serve binary support ELF targets, embed your local build into the placeholder before go build:

cd guest && make prepare-release-embed   # copies the real build output over the tracked
                                          # placeholders at guest/embedded/ — this is a real,
                                          # multi-megabyte binary; never `git add` it
cd .. && go build -o cascade ./cmd/cascade

applevz also needs the com.apple.security.virtualization entitlement, invalidated by any rebuild:

codesign --entitlements applevz/entitlements.plist -s - ./cascade

By default every ELF run boots the "recon" guest image. Pass -analysis-depth deep to cascade run to boot a second, larger image instead — adds AFL++ (QEMU mode, native aarch64 target fuzzing) and Ghidra headless (function-list summaries; full output goes to guest scratch, read more with a follow-up sh -c "sed -n '...'" call) on top of every recon-tier tool, at a longer wall-clock budget (150s vs. 60s per command).

The deep-tier image is real and much larger than recon's. Measured against the actual built images, not estimated:

compressed uncompressed
initramfs.cpio.gz (recon) 137.2 MiB 284.4 MiB
initramfs-deep.cpio.gz (deep) 595.1 MiB 1179.9 MiB

The uncompressed figure is the one that matters at runtime. applevz boots with vz.WithInitrd and no block device, so the archive is the guest's root filesystem: the kernel unpacks all of it into unevictable ramfs before /init runs. Guest RAM has to exceed that before a single command executes, which is why every ELF run's resources are floored by which image it boots, regardless of how small the target file is (applyGuestImageLimits in cmd/cascade/provision.go):

  • recon → at least sandbox.TierMedium (2 vCPU / 1024 MB)
  • deep → at least sandbox.TierLarge (4 vCPU / 4096 MB)

Both floors are pinned by real VM boot tests (applevz's TestBackend_ReconImage_* and TestBackend_DeepImage_*, run via make -C applevz test-integration). Below its floor the deep image simply fails to boot; the recon image does something worse — it boots and answers on vsock, but unpacking ran out of memory before /bin/busybox was written, so every command comes back executable file not found in $PATH.

Building the deep image is itself expensive (~1.5 GB of downloads, a multi-hour first build — Ghidra's arm64 natives and AFL++'s patched QEMU are both compiled from source); see guest/README.md.

Two current limitations worth knowing:

  • -analysis-depth exists only on cascade run. cascade serve and the web UI have no equivalent control, so every server-started run is a recon run. A deep run started from the CLI that pauses on budget exhaustion does resume at deep depth — its depth is persisted on the run record (runstore.RunMeta.AnalysisDepth) precisely so resuming cannot silently downgrade it.
  • Deep depth is applevz/ELF only. Passing it for a Mach-O target prints a warning and runs recon; Phase 3's deep-tier tools are Linux-guest binaries.

See docs/superpowers/plans/2026-08-04-tool-catalog-phase3.md for the full design.

Vendoring extra macseatbelt analysis tools

The macseatbelt (Mach-O) substrate has the same "a plain build gets you less than it could" gap as the ELF path above, just quieter about it: macseatbelt.PresentAnalysisTools() filters its full eleven-tool manifest down to whatever actually resolves on the host, and a plain go build leaves only two of those eleven resolvablefile and codesign, both already part of the base macOS install. The other nine (ent, checksec, the six llvm-objdump/llvm-nm/ llvm-strings/llvm-readobj/llvm-lipo/llvm-cxxfilt binutils tools, and rabin2) are built from source and vendored by this repo, and there is no runtime warning when they're missing — a scan still runs fine, the agent is just silently offered 9 fewer tools than the substrate can actually provide.

cd macseatbelt/tools && make all   # builds ent + checksec.rs (a few seconds) plus the six
                                    # llvm-* binutils tools from the LLVM source tree — the slow
                                    # part, ~10 minutes the first time (LLVM itself dominates that,
                                    # not these six tools; cached/incremental rebuilds after the
                                    # first are much faster) — plus rabin2, built from radare2's
                                    # source tree (~4 minutes 16 seconds the first time, a real
                                    # observed clean-build time, not an estimate). Needs Xcode
                                    # Command Line Tools, a Rust toolchain, and cmake/ninja on the
                                    # build host; see the Makefile's own header comment for exact
                                    # prerequisite versions this was last verified against.

Then install the built binaries where macseatbelt.vendoredToolsDir() looks for them at runtime: a macseatbelt-tools/ directory resolved as a sibling of the running cascade binary itself (through symlinks, same as every other host path this package trusts):

make -C macseatbelt/tools install DESTDIR=/path/to/dir/holding/cascade   # DESTDIR is the directory
                                                                          # *containing* cascade,
                                                                          # e.g. DESTDIR=. if
                                                                          # cascade is in the
                                                                          # current directory —
                                                                          # NOT macseatbelt-tools/
                                                                          # itself, which the
                                                                          # install recipe creates
                                                                          # for you inside DESTDIR.

Skip this and a locally-built cascade still scans Mach-O binaries fine — the agent just sees file/codesign and nothing else until you run these two commands and point DESTDIR at wherever your built cascade binary actually lives.

CI

.github/workflows/guest-verify.yml runs make -C guest verify (checks a built initramfs against guest.DefaultManifest()) and both noticegen -check commands on every PR touching guest/, toolcatalog/, or NOTICE.txt. This is the design doc's Phase-2-onward gate: no tool ships past this point without a green run.

Tests

go test ./...            # everything that needs no VM/API key — protocol, limits, budget,
                          # provisioning logic, phase handlers against a fake LLM client, etc.

Real-VM integration suites are gated so the above stays fast and hermetic: applevz's skip unless CASCADE_VZ_INTEGRATION=1, experiment's is behind an integration build tag. Both need guest/Image/guest/initramfs.cpio.gz (see above) and code-signing, and both have a Makefile that handles the go test -ccodesign → run sequence:

cd applevz && make test-integration     # boot, exec, wall-clock kill, snapshot, restore, teardown
cd experiment && make test-integration  # the runner's fork-per-attempt fallback, on a real guest

Frontend:

cd web && npm test && npx tsc --noEmit

A known flake

A fork's first Exec immediately after RestoreFrom intermittently fails at the transport level with broker: read length prefix: EOF. Pre-existing, understood to be transport-level rather than a protocol bug. If an integration run trips it, re-run.

Layout

sandbox/       backend interface, resource limits, the resource-tier ladder + EscalatingBackend
broker/        wire protocol + guest-side server
cmd/broker/    the guest binary (linux/arm64, runs as PID 1)
guest/         kernel + initramfs build, and the release-embedding step (see guest/README.md)
applevz/       Virtualization.framework backend (ELF/Linux)
macseatbelt/   Seatbelt process-sandbox backend (Mach-O)
agent/         the ReAct tool-calling loop + run_in_sandbox tool
classify/      classify phase handler
loadtest/      load-test phase handler
pentest/       pentest phase handler
controller/    phase-graph orchestration
engine/        phase dispatcher
findings/      canonical finding schema
observe/       the event schema + observer interface every frontend renders from
runstore/      run persistence + event log + SSE feed
budget/        run budget + category fallback
experiment/    snapshot-fork experiment runner
config/        API key / base URL / model resolution
webserver/     HTTP API + embedded frontend assets
web/           React frontend source
cmd/cascade/   the CLI (smoketest, run, serve)
docs/          PRD, design specs, and implementation plans

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages