Document portable limits primitives
ober
b96df2921a9d16f80c6a07a799b691d503356eda
new file mode 100644 --- /dev/null +++ b/docs/limits.md @@ -0,0 +1,442 @@ +# Portable Limits and Capability Primitives + +This document lists the Jerboa-side work needed to support capability-wrapped launchers such as `jsh` wrappers for Node/npm-backed AI tools. The goal is to provide portable, low-level security and process primitives in Jerboa. User-facing policy syntax, comma-prefixed launch-script directives, profiles, and tool-specific wrappers should live in `jsh`. + +## Scope + +Jerboa should provide: + +- portable sandbox enforcement +- portable process supervision +- portable resource limits +- executable identity inspection +- filesystem access tracing +- network allowlist/proxy helpers +- environment and secret handling +- temporary home/cache helpers +- structured audit data + +Jerboa should not provide: + +- `jsh` comma-rule syntax +- tool profiles such as `codex`, `claude`, or `opencode` +- npm-specific policy decisions +- transactional source workspace UX + +Those belong in `jsh` or higher-level applications. + +## Threat Model + +The motivating case is a trusted Jerboa program launching an untrusted or semi-trusted child process, especially a runtime with supply-chain exposure such as Node/npm. The child may: + +- read broad filesystem locations looking for secrets +- run install scripts or nested package managers +- spawn subprocesses +- modify project files, shell config, Git hooks, or tool config +- open arbitrary network connections +- consume unbounded CPU, memory, pids, disk, or output + +The trusted parent must prepare a narrow execution environment, start the child with limited authority, monitor it, and produce an audit trail. + +Important constraint: once kernel sandboxing is installed in a process, access can generally only become stricter. Broader access requires the trusted parent to start a new child with a broader policy. + +## 1. Unified Sandbox API + +Jerboa already has platform pieces under modules such as `(std os sandbox)`, `(std os seccomp)`, `(std os landlock)`, `(std security sandbox)`, `(std security landlock)`, `(std security seccomp)`, `(std security capsicum)`, and `(std security seatbelt)`. The missing piece is a single capability-oriented API with consistent semantics. + +Proposed module: + +```scheme +(import (std os limits sandbox)) +``` + +Responsibilities: + +- accept read, write, execute, and network policy +- apply the strongest available backend for the current OS +- report exact support level before launch +- distinguish full, partial, unavailable, and degraded enforcement +- expose structured errors for unsupported policy features + +Required platform backends: + +| Platform | Filesystem | Syscalls/Process | Notes | +| --- | --- | --- | --- | +| Linux | Landlock | seccomp | best target for full support | +| OpenBSD | unveil | pledge | good model for shrink-only policy | +| FreeBSD | Capsicum | rlimit/process controls | requires pre-opened resources | +| macOS | Seatbelt profile | rlimit/process controls | private/limited APIs; support may be partial | +| Other | none or partial | rlimit only | must report degraded mode | + +Support query should be first-class: + +```scheme +(sandbox-capabilities) +;; => alist/record describing fs, exec, net, syscall, trace, limits support +``` + +The API should make degraded behavior explicit. A caller must be able to say "fail closed if full filesystem confinement is unavailable." + +## 2. Process Supervision + +Jerboa needs a portable process supervisor that can launch and control child process trees. This is the foundation for timeouts, tracing, audit, and tree kill. + +Proposed module: + +```scheme +(import (std os supervise)) +``` + +Required features: + +- launch a child in a new process group/session where supported +- pass an explicit environment +- set cwd +- wire stdin/stdout/stderr +- wait for structured exit status +- kill one child or the whole process group +- apply timeout from the parent +- collect basic resource and process-tree metadata + +Suggested result shape: + +```scheme +(process-result + status: 0 + signal: #f + pid: 1234 + command: '("/usr/bin/node" "tool.js") + elapsed-ms: 1250 + stdout-bytes: 4096 + stderr-bytes: 512) +``` + +Tracking subprocesses is platform-specific. Jerboa should expose the best available method and report precision: + +- exact tree tracking +- process-group only +- best-effort sampling +- unavailable + +## 3. Resource Limits + +`jsh` currently has memory limiting logic. Jerboa should generalize this into a portable stdlib facility. + +Proposed module: + +```scheme +(import (std os limits)) +``` + +Limit types: + +- memory bytes +- CPU seconds +- wall-clock milliseconds +- process count +- open file descriptors +- file size +- stdout/stderr byte count +- core dump size + +Backends: + +- Linux cgroup v2 for memory, pids, CPU, and process-tree containment +- `setrlimit` fallback on Linux/BSD/macOS +- parent-side output counters for stdout/stderr limits +- parent-side wall-clock timeout + +Important caveats: + +- `RLIMIT_AS` is not the same as physical memory. +- `RLIMIT_NPROC` may affect a user, not just a tree. +- cgroup setup may require permissions or delegated controllers. +- output limits are best enforced by the supervising parent. + +The API should return which limits were actually installed. + +## 4. Executable Identity + +Name-based policy is ergonomic but weak. Jerboa needs an executable identity API so callers can flag `node`, `npm`, `npx`, `bun`, etc. by resolved identity. + +Proposed module: + +```scheme +(import (std os exec-id)) +``` + +Required operations: + +- resolve a command name through an explicit `PATH` +- return absolute path +- canonicalize via realpath +- stat device/inode +- compute optional SHA-256 hash +- detect symlinks +- preserve original argv name for audit + +Suggested identity shape: + +```scheme +(exec-id + argv0: "node" + path: "/usr/local/bin/node" + realpath: "/opt/homebrew/bin/node" + dev: 16777234 + ino: 12345678 + sha256: "...") +``` + +This lets higher-level tools implement rules such as "flag anything resolving to this Node binary as hostile" without relying only on process name. + +## 5. Filesystem Access Tracing + +Jerboa should expose a portable `tracefs` API that records file and directory access by a supervised command and its subprocesses. + +Proposed module: + +```scheme +(import (std os tracefs)) +``` + +Common event format: + +```scheme +(fs-event + pid: 1234 + ppid: 1200 + exe: "/usr/bin/node" + op: 'read + path: "/repo/package.json" + cwd: "/repo" + fd: #f + result: 'ok + errno: #f + timestamp-ms: 123456789) +``` + +Operations to classify: + +- read/open +- write/open +- create +- delete +- rename +- metadata/stat/access +- directory listing +- symlink/readlink +- execute + +The tracer must maintain enough process state to resolve relative paths: + +- cwd per process +- fd table per process +- fd inheritance across fork/clone +- `openat` directory fd mappings +- exec identity changes + +Backends: + +| Platform | Prototype | Better backend | Notes | +| --- | --- | --- | --- | +| Linux | `strace -f -e trace=file` parser | native `ptrace`, optional eBPF | best practical starting point | +| macOS | `dtrace` where allowed | EndpointSecurity | SIP/entitlements may restrict support | +| FreeBSD | `truss`/`ktrace` | native ktrace parser | good fit for process tracing | +| OpenBSD | `ktrace`/`ktruss` if available | native parser | combine with pledge/unveil denial info | + +If deep tracing is unavailable, Jerboa should expose degraded modes: + +- sandbox denial events only +- wrapper-level explicit file opens only +- final directory diff +- unsupported + +The output format must stay the same even when event completeness differs. + +## 6. Network Allowlist Support + +Kernel sandbox APIs are usually good at "network yes/no" but poor at portable host allowlists. Jerboa should provide a small local proxy toolkit so callers can deny direct network in the child and allow only proxy-mediated destinations. + +Proposed module: + +```scheme +(import (std net allow-proxy)) +``` + +Required features: + +- HTTP CONNECT proxy +- optional SOCKS5 proxy +- allow `host:port` +- deny IP literals +- deny loopback, link-local, private, and local network ranges +- resolve DNS in the trusted parent/proxy +- re-check resolved addresses against deny rules +- structured connection audit events + +Typical flow: + +1. parent starts allowlist proxy on loopback or Unix socket +2. parent denies direct child network through sandbox policy +3. parent injects `HTTPS_PROXY`, `HTTP_PROXY`, or tool-specific proxy env +4. proxy allows only configured destinations + +The proxy should be a library primitive, not a `jsh`-only feature. + +## 7. Environment and Secret Broker + +Jerboa needs helpers to construct a clean child environment from an allowlist and inject explicit secrets safely. + +Proposed module: + +```scheme +(import (std security env)) +``` + +Required features: + +- create environment from scratch +- allow selected variables +- deny patterns such as `*TOKEN*`, `*KEY*`, `AWS_*` +- inject named secrets +- redact secrets from logs and audit output +- optionally mark secrets as runtime-only, not install-phase + +Secret storage itself can remain caller-provided. Jerboa should provide safe transport, redaction, and environment construction primitives. + +Useful helpers: + +- redact a string by registered secret values +- redact an alist/env vector +- produce an audit-safe environment summary +- detect accidental secret leakage in command arguments + +## 8. Temporary Home and Cache Helpers + +Jerboa should provide standard helpers for fake home and per-run cache directories. + +Proposed module: + +```scheme +(import (std os temp-home)) +``` + +Required features: + +- create isolated fake `HOME` +- create named cache directories +- choose cleanup or preserve-on-error behavior +- copy selected config files into fake home +- produce paths suitable for sandbox read/write grants +- record all generated paths in audit metadata + +This keeps Node/npm-like tools away from the real home directory by default. + +## 9. Structured Audit Model + +All primitives should emit compatible audit records. Higher-level applications should not need to parse human text. + +Proposed module: + +```scheme +(import (std security audit-log)) +``` + +Record types: + +- process start/exit +- sandbox installed/degraded/failed +- limit installed/degraded/failed +- executable resolved +- filesystem event +- network event +- environment summary +- secret injected/redacted +- policy violation + +Formats: + +- in-memory list/stream of records +- JSON Lines writer +- human summary renderer + +Audit data must avoid leaking secret values. + +## 10. Policy-Neutral Data Types + +Jerboa should define policy-neutral records that `jsh` can consume: + +- sandbox policy +- sandbox capability report +- limit policy +- limit installation result +- executable identity +- process launch spec +- process result +- filesystem event +- network event +- audit event + +These records should avoid `jsh` terms such as comma rules or profiles. Jerboa is the enforcement substrate; `jsh` is one frontend. + +## 11. Testing Requirements + +Each module needs platform-aware tests with skip/degraded behavior. + +Core tests: + +- sandbox denies unreadable path +- sandbox allows explicitly readable path +- sandbox denies writes outside allowlist +- process supervisor kills entire process group +- wall timeout works +- output byte limit works +- executable identity resolves symlinks +- fake home prevents reading real home config +- env broker strips token-like variables +- redaction removes registered secret values +- tracefs sees basic open/stat/exec events where supported +- proxy allows configured host and denies localnet + +Tests should assert support reports, not pretend every platform has every backend. + +## 12. Implementation Order + +Recommended order: + +1. Capability reports for existing sandbox/limit modules. +2. Process supervisor with process-group kill and structured status. +3. General resource limit API using existing cgroup/rlimit code. +4. Executable identity API. +5. Environment/secret broker and redaction helpers. +6. Temporary home/cache helpers. +7. Network allowlist proxy. +8. Tracefs API with Linux `strace` prototype. +9. Native tracing backends. +10. Unified audit record model across all pieces. + +This order gives `jsh` useful building blocks early, before the hardest portable tracing work is complete. + +## 13. Relationship to jsh + +`jsh` should use these primitives to implement: + +- comma-prefixed launch-script rules +- named profiles +- named directory capabilities +- Node/npm shims +- transactional workspace mode +- user-facing commands such as `,read`, `,write`, `,net`, `,limit`, and `,tracefs` + +Jerboa should not know about those user-facing commands. Jerboa should only know how to enforce and report portable limits. + +## 14. Non-Goals + +- Perfect confinement on every OS. +- Live sandbox loosening after a child starts. +- Replacing AppArmor, SELinux, or platform MAC systems. +- Root-only tracing as the only path. +- npm-specific logic in the Jerboa stdlib. + +The design should be honest about partial support. A caller must be able to require fail-closed behavior when degraded enforcement is not acceptable. +