Files
Hermit/docs/compat.md
T
WHC 32b4afd772
ci / linux (clang) (push) Successful in 4m44s
ci / linux (gcc) (push) Successful in 5m36s
ci / musl (push) Failing after 29s
ci / windows-cross (push) Successful in 31m37s
v0.7.1: allow network inside the run_shell sandbox
Landlock no longer handles TCP, the Seatbelt profile drops (deny network*),
and the seccomp filter stops refusing IPv4/IPv6 sockets. Sandboxed commands
run without asking in auto mode on every backend (Low-IL no longer asks for
network commands), and connection errors are not treated as sandbox blocks.
2026-10-08 21:05:14 +08:00

9.5 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. The network is not restricted.

Platform Sandbox Notes
Linux 5.13+ Landlock (raw syscalls, no extra library)
Linux 5.5+ without Landlock (Android / Termux, WSL, older distros), x86_64 / aarch64 seccomp user-notify see below
macOS sandbox_init profile
Windows 10 1903+ Low integrity restricted token + Job Object writes need a Low label (given to the workspace once); see README-windows.md
Older kernels none falls back to the read-only rules

The status bar and sidebar show auto · landlock, auto · seccomp, auto · seatbelt, auto · lowil or auto · rules. CST_SANDBOX=off disables the sandbox; CST_SANDBOX=landlock, seccomp or lowil 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. The probe also checks that the parent can peek a direct child's memory (process_vm_readv or /proc/<pid>/mem); with Yama ptrace_scope=2 and no CAP_SYS_PTRACE that fails, so seccomp is not selected (forced CST_SANDBOX=seccomp then falls back to unavailable). 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 and mouse selection use OSC 52. iTerm2 needs "Applications in terminal may access clipboard"; tmux needs set -g set-clipboard on; Windows Terminal (WSL), kitty, WezTerm, foot and WHC Shell support it out of the box.
  • Mouse selection: the TUI turns on button-motion reporting (?1002h) on top of what ncurses enables, so dragging over the transcript selects text (double-click a word, triple-click a line) and releasing copies it. Terminals without 1002 still get clicks and the wheel. Shift+drag bypasses the TUI and uses the terminal's own selection.
  • 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.
  • Kitty keyboard protocol: at startup the TUI sends CSI ? u; if the terminal answers (kitty, WezTerm, foot, Ghostty, WHC Shell, ...) it pushes the "disambiguate" flag (CSI > 1 u) on the alternate screen and pops it on exit. That makes Shift+Enter / Ctrl+Enter (newline), Ctrl+Backspace (delete word) and an instant Esc work. Other terminals (and tmux) don't answer and keep the legacy keys. If the TUI is killed, the main screen's keyboard mode is untouched because the flags live on the alternate screen's stack.

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
  • Drag over a wrapped reply: it highlights, the status row says "Copied", and pasting gives the paragraph without extra line breaks; double-click copies a word
  • 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/Windows); 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

Windows specifics (scripts/win_test.sh runs the tests on English and Chinese VMs as Administrator, a standard user and a Chinese user name):

  • install_windows.ps1 -Zip dist\cst-agent-*.zip installs, adds the user PATH; cst-agent-update -Zip ... works while cst-agent.exe runs
  • conhost with a raster font: Chinese renders (font switched), and the old font is back after exit; Windows Terminal renders the same
  • Chinese input via IME, paste, mouse click / drag-copy / wheel
  • Workspace under C:\Users\<中文名>\项目: read/write/edit/apply_patch keep CRLF and BOM; run_shell output in Chinese is readable on a 936 system
  • auto · lowil: first command reports the label; echo x > C:\outside asks to rerun; curl ... runs without asking; /sandbox reset-labels restores the label
  • Esc during sleep 30 & sleep 30 returns at once and leaves no busybox.exe behind

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