Files
Hermit/docs/compat.md
T
WHC daa92a6cfb
ci / linux (gcc) (push) Canceled after 0s
ci / sanitizers (push) Canceled after 0s
ci / musl (push) Canceled after 0s
ci / macos (push) Canceled after 0s
ci / linux (clang) (push) Canceled after 4m58s
seccomp: require child memory peek in the availability probe
With Yama ptrace_scope=2 and no CAP_SYS_PTRACE, process_vm_readv and
/proc/<pid>/mem both fail, so sc_decide cannot read path strings and every
workspace write gets EACCES. The old probe only checked the USER_NOTIF
round trip, so CST_SANDBOX=seccomp and the forced-seccomp test pass looked
available and then failed on the first write.

sc_can_peek() forks a child, places a marker on its stack, and retries the
same sc_peek path. sc_probe() now needs that too, so those hosts fall back
to none (or keep landlock) instead of a silent deny-all sandbox.
2026-10-07 01:39:08 +08:00

177 lines
9.6 KiB
Markdown

# 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 |
| Windows 10 1903+ | Low integrity restricted token + Job Object | writes need a Low label (given to the workspace once); network is not blocked, so network commands still ask; 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 ...` asks before running; `/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