skate/PLAN.md
2026-03-01 19:20:43 -08:00

96 lines
6 KiB
Markdown

# Implementation Plan
## Phase 4: Sandboxing
### Step 4.1: Create sandbox module with policy types and tracing foundation
- `SandboxPolicy` struct: read-only paths, read-write paths, network allowed bool
- `Sandbox` struct holding policy + working dir
- Add `tracing` spans and events throughout from the start:
- `#[instrument]` on all public `Sandbox` methods
- `debug!` on policy construction with path lists
- `info!` on sandbox creation with full policy summary
- No enforcement yet, just the type skeleton and module wiring
- **Files:** new `src/sandbox/mod.rs`, `src/sandbox/policy.rs`
- **Done when:** compiles, unit tests for policy construction, `RUST_LOG=debug cargo test` shows sandbox trace output
### Step 4.2: Landlock policy builder with startup gate and tracing
- Translate `SandboxPolicy` into Landlock ruleset using `landlock` crate
- Kernel requirements:
- **ABI v4 (kernel 6.7+):** minimum required -- provides both filesystem and network sandboxing
- ABI 1-3 have filesystem only, no network restriction -- tools could exfiltrate data freely
- Startup behavior -- on launch, check Landlock ABI version:
- ABI >= 4: proceed normally (full filesystem + network sandboxing)
- ABI < 4 (including unsupported): **refuse to start** with clear error: "Landlock ABI v4+ required (kernel 6.7+). Use --yolo to run without sandboxing."
- `--yolo` flag: skip all Landlock enforcement, log `warn!` at startup, show "UNSANDBOXED" in status bar permanently
- Landlock applied per-child-process via `pre_exec`, NOT to the main process
- Main process needs unrestricted network (Claude API) and filesystem (provider)
- Each `exec_command` child gets the current policy at spawn time
- `:net on/off` takes effect on the next spawned command
- Tracing:
- `info!` on kernel ABI version detected
- `debug!` for each rule added to ruleset (path, access flags)
- `warn!` on `--yolo` mode ("running without kernel sandboxing")
- `error!` if ruleset creation fails unexpectedly
- **Files:** `src/sandbox/landlock.rs`, add `landlock` dep to `Cargo.toml`, update CLI args in `src/app/`
- **Done when:** unit test constructs ruleset without panic; `--yolo` flag works on unsupported kernel; startup refuses without flag on unsupported kernel
### Step 4.3: Sandbox file I/O API with operation tracing
- `Sandbox::read_file`, `Sandbox::write_file`, `Sandbox::list_directory`
- Move `validate_path` from `src/tools/mod.rs` into sandbox
- Tracing:
- `debug!` on every file operation: requested path, canonical path, allowed/denied
- `trace!` for path validation steps (join, canonicalize, starts_with check)
- `warn!` on path escape attempts (log the attempted path for debugging)
- `debug!` on successful operations with bytes read/written
- **Files:** `src/sandbox/mod.rs`
- **Done when:** unit tests in tempdir pass; path traversal rejected; `RUST_LOG=trace` shows full path resolution chain
### Step 4.4: Sandbox command execution with process tracing
- `Sandbox::exec_command(cmd, args, working_dir)` spawns child process with Landlock applied
- Captures stdout/stderr, enforces timeout
- Tracing:
- `info!` on command spawn: command, args, working_dir, timeout
- `debug!` on command completion: exit code, stdout/stderr byte lengths, duration
- `warn!` on non-zero exit codes
- `error!` on timeout or spawn failure with full context
- `trace!` for Landlock application to child process thread
- **Files:** `src/sandbox/mod.rs` or `src/sandbox/exec.rs`
- **Done when:** unit test runs `echo hello` in tempdir; write outside sandbox fails (on supported kernels)
### Step 4.5: Wire tools through Sandbox
- Change `Tool::execute` signature to accept `&Sandbox` instead of (or in addition to) `&Path`
- Update all 4 built-in tools to call `Sandbox` methods instead of `std::fs`/`std::process::Command`
- Remove direct `std::fs` usage from tool implementations
- Update `ToolRegistry` and orchestrator to pass `Sandbox`
- Tracing: tools now inherit sandbox spans automatically via `#[instrument]`
- **Files:** `src/tools/*.rs`, `src/tools/mod.rs`, `src/core/orchestrator.rs`
- **Done when:** all existing tool tests pass through Sandbox; no direct `std::fs` in tool files; `RUST_LOG=debug cargo run` shows sandbox operations during tool execution
### Step 4.6: Network toggle
- `network_allowed: bool` in `SandboxPolicy`
- `:net on/off` TUI command parsed in input handler, sent as `UserAction::SetNetworkPolicy(bool)`
- Orchestrator updates `Sandbox` policy. Status bar shows network state.
- Only available when Landlock ABI >= 4 (kernel 6.7+); command hidden otherwise
- Status bar shows: network state when available, "UNSANDBOXED" in `--yolo` mode
- Tracing: `info!` on network policy change
- **Files:** `src/tui/input.rs`, `src/tui/render.rs`, `src/core/types.rs`, `src/core/orchestrator.rs`, `src/sandbox/mod.rs`
- **Done when:** toggling `:net` updates status bar; Landlock network restriction applied on ABI >= 4
### Step 4.7: Integration tests
- Tools + Sandbox in tempdir: write confinement, path traversal rejection, shell command confinement
- Skip Landlock-specific assertions on ABI < 4
- Test `--yolo` mode: sandbox constructed but no kernel enforcement
- Test startup gate: verify error on ABI < 4 without `--yolo`
- Tests should assert tracing output where relevant (use `tracing-test` crate or `tracing_subscriber::fmt::TestWriter`)
- **Files:** `tests/sandbox.rs`
- **Done when:** `cargo test --test sandbox` passes
### Phase 4 verification (end-to-end)
1. `cargo test` -- all tests pass
2. `cargo clippy -- -D warnings` -- zero warnings
3. `RUST_LOG=debug cargo run -- --project-dir .` -- ask Claude to read a file, observe sandbox trace logs showing path validation and Landlock policy
4. Ask Claude to write a file outside project dir -- sandbox denies with `warn!` log
5. Ask Claude to run a shell command -- observe command spawn/completion trace
6. `:net off` then ask for network access -- verify blocked
7. Without `--yolo` on ABI < 4: verify startup refuses with clear error
8. With `--yolo`: verify startup succeeds, "UNSANDBOXED" in status bar, `warn!` in logs