Files
Hermit/docs/compat.md
T
WHC 8efd63bb31
ci / linux (clang) (push) Failing after 1m48s
ci / linux (gcc) (push) Failing after 20s
ci / sanitizers (push) Failing after 22s
ci / musl (push) Failing after 32s
ci / macos (push) Canceled after 0s
Sandbox: seccomp user-notify fallback for Termux and kernels without Landlock
- Child installs a seccomp filter with a listener; a supervisor thread
  resolves the path of write-type syscalls (cwd, dirfd, .., symlinks,
  /proc/self, /dev/stdout, both ends of rename/link) and answers
  CONTINUE or EACCES; IPv4/IPv6 sockets are refused
- Probe order Landlock -> seccomp -> none, checked in a throwaway child;
  CST_SANDBOX=landlock|seccomp|off
- curl connect failures count as sandbox refusals
- test_sandbox runs the detected sandbox and forced seccomp, with escape,
  symlink, rename, link, chmod, stdout, ENOENT, background and network cases
- Docs: platform matrix, limits (not a security boundary, Yama and
  background jobs)
2026-10-06 00:25:24 +08:00

7.1 KiB

Platform support: Linux, WSL, macOS

CST-Agent targets POSIX systems: Linux (glibc and musl), WSL 2 and macOS. Native Windows builds are covered separately in README-windows.md.

Build

Platform Dependencies Build
Debian / Ubuntu / WSL build-essential pkg-config libcurl4-openssl-dev libncurses-dev make
Fedora gcc make pkgconf libcurl-devel ncurses-devel make
Arch base-devel curl ncurses make
Alpine (musl) build-base pkgconf curl-dev ncurses-dev make
NixOS nix-shell (uses shell.nix) make
macOS xcode-select --install, brew install ncurses curl pkg-config make

make install PREFIX=~/.local installs the binary; CMake works too (cmake -B build && cmake --build build && ctest --test-dir build).

The Makefile picks ncursesw, then ncurses, through pkg-config. On macOS it adds Homebrew's keg-only ncurses/curl to PKG_CONFIG_PATH; without Homebrew it falls back to the system -lncurses (wide-character capable).

Verified so far: NixOS x86_64 with gcc and clang (-Werror, make check, make asan, ctest), and every source compiled -fsyntax-only with x86_64-unknown-linux-musl-gcc without warnings. Full musl, macOS and WSL builds run in CI (.gitea/workflows/ci.yml; the macOS job needs a runner and the HAVE_MACOS_RUNNER variable) or by hand with the checklist below.

Files

What Where
API keys (one per provider, entered in the TUI or with --ask-key) $XDG_CONFIG_HOME/cst-agent/credentials (default ~/.config/cst-agent/), mode 0600
Last provider, models, effort and approval mode $XDG_CONFIG_HOME/cst-agent/settings.json
Sessions, undo snapshots, exports <workspace>/.agent/ (git-ignored automatically)
Saved permission rules <workspace>/.agent/permissions.json
Project notes injected into the prompt <workspace>/AGENTS.md (or CST.md)

Shell sandbox

In the default auto approval mode, run_shell runs inside an OS sandbox when one is available. Commands may read and execute anything but may only write beneath the workspace, the temp directory, /dev/null and /dev/tty.

Platform Sandbox Notes
Linux 5.13+ Landlock (raw syscalls, no extra library) ABI ≥ 4 (6.7+) also blocks TCP connect/bind
Linux 5.5+ without Landlock (Android / Termux, WSL, older distros), x86_64 / aarch64 seccomp user-notify refuses IPv4/IPv6 sockets; see below
macOS sandbox_init profile also denies network
Older kernels, Windows none falls back to the read-only rules

The status bar and sidebar show auto · landlock, auto · seccomp, auto · seatbelt or auto · rules. CST_SANDBOX=off disables the sandbox; CST_SANDBOX=landlock or CST_SANDBOX=seccomp allows only that kind. When a sandboxed command looks blocked, the TUI asks whether to rerun it outside the sandbox. The policy is built in the parent; the forked child only makes a few async-signal-safe syscalls before exec, so the agent itself is never confined.

seccomp. Used when Landlock is missing, e.g. Android GKI kernels. The child installs a seccomp filter with a user-notify listener and passes the listener to a supervisor thread in the agent. Opens with write flags and every create / remove / rename / link / chmod / chown / utimes / xattr call stop until the supervisor has read the path from the child's memory, resolved it (cwd, dirfd, .., symlinks, /proc/self, /dev/stdout) and answered: continue when it lands in the allowed places, EACCES otherwise. Reads, exec and writes to already-open files are not checked. Background jobs that outlive their shell are no longer the agent's descendants: with Yama ptrace_scope ≥ 1 (common on desktop distros, absent on Android) the supervisor cannot read their memory and their checked writes fail with EACCES; once the run has finished they get ENOSYS. This keeps mistakes out of the rest of the disk but is not a security boundary: the kernel reads the path again after the check, so a hostile multi-threaded program can swap it in between. The availability check runs in a throwaway child, so a platform seccomp policy that kills the caller cannot take the agent down.

Terminal notes

  • UTF-8 locale gives the full UI. Under a non-UTF-8 locale, or with CST_ASCII=1, box and icon glyphs fall back to ASCII.
  • NO_COLOR=1 turns colours off.
  • /copy uses OSC 52. iTerm2 needs "Applications in terminal may access clipboard"; tmux needs set -g set-clipboard on; Windows Terminal (WSL) supports it out of the box.
  • Bracketed paste needs ncurses (all platforms above).
  • macOS Terminal.app sends Option+Enter as Esc+Enter only with "Use Option as Meta key"; Ctrl+N always inserts a newline.

Acceptance checklist

Run on each platform before a release. Use a scratch git repository as the workspace and a real API key (entered when the TUI asks for it).

Build and tests:

  • make has no warnings; make check passes
  • make asan passes (Linux; macOS: make CFLAGS="-fsanitize=address,undefined -g" check)
  • cmake -B build && cmake --build build && ctest --test-dir build passes
  • make install PREFIX=/tmp/cst && /tmp/cst/bin/cst-agent --help

CLI:

  • --list-sessions, --new, --resume <prefix>, --continue behave as documented
  • --headless --prompt "list the files" prints a transcript and exits 0
  • First start asks for a provider and key; the credentials file is mode 0600
  • Second start reuses provider, model and effort without asking

TUI:

  • Typing Chinese/emoji keeps the cursor in the right column; ←→ Home End work
  • Pasting a multi-line block does not send it; Enter sends
  • / shows the command popup, Tab completes, @ completes file paths
  • Esc cancels a running turn; Esc on an empty prompt does nothing; Ctrl+C twice quits
  • A long run_shell (e.g. sleep 30) stops promptly on Esc
  • Write permission shows the diff; a saves a rule; p saves a shell prefix rule
  • ask_user modal accepts a number, an arrow selection and typed text
  • Todo panel appears while tasks are open; Ctrl+T hides it
  • /undo restores files changed by the last turn; /retry re-asks
  • /diff, /export, /status, /tools off grep, /yolo work
  • Mouse: click a tool card to expand it, click in the input to move the cursor, click a picker row; /mouse turns it off
  • Tab toggles Build/Plan, Shift+Tab cycles ask/auto/yolo, Ctrl+P opens the palette
  • /model lists the provider's /models (marked (api)); a wrong key is re-asked
  • auto mode: echo x > ~/outside in run_shell asks "Rerun outside the sandbox?" (Linux/Termux/macOS); with CST_SANDBOX=off, ls runs and touch x asks
  • Resize the terminal during streaming: layout recovers
  • NO_COLOR=1 and CST_ASCII=1 render readable screens

WSL specifics:

  • Workspace under /mnt/c/... works (slower IO is expected); ~/project is recommended
  • Windows Terminal: mouse wheel scroll and OSC 52 /copy

macOS specifics:

  • Builds with Homebrew ncurses and with the system ncurses (brew unlink ncurses)
  • Terminal.app and iTerm2: arrows, Home/End, PgUp/PgDn, bracketed paste