docs: align README and AGENTS.md with single-crate rewrite
Both files described the old multi-crate architecture with firewall
container, nftables, and bridge server. None of that exists here.
Assisted-by: Claude Opus 4.6 via pi
diff --git a/AGENTS.md b/AGENTS.md
index 65d9de2..e8e9505 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -2,53 +2,50 @@
## Project
-Ramekin is a containerized harness for running the [pi coding agent](https://github.com/badlogic/pi-mono) with network-restricted access. A Rust CLI orchestrates two Docker containers that share a network namespace: an **agent** container running pi and a **firewall** container enforcing nftables rules that restrict all outbound traffic to `api.anthropic.com:443`.
+Ramekin is a containerized harness for running the [pi coding agent](https://github.com/badlogic/pi-mono) with network-restricted access. A Rust CLI builds a Docker image, generates a compose config at runtime, and attaches the user's terminal to the agent container.
## Repository layout
```
-Cargo.toml # Workspace root + ramekin CLI crate
-src/main.rs # CLI: generates compose config, starts containers, attaches to pi
-Dockerfile # Agent container (Node.js + pi)
-firewall/
- Cargo.toml # ramekin-firewall crate (bridge server)
- Dockerfile # Firewall container (Rust binary + nftables)
- entrypoint.sh # Resolves Anthropic IPs, loads nftables, starts bridge
- src/main.rs # Axum bridge server (/echo endpoint)
-justfile # Local dev tasks (check, fmt, clippy, cov)
-.github/workflows/ # CI (fmt + check + clippy + test) and CalVer release
+Cargo.toml # Single-crate workspace
+src/main.rs # CLI: builds image, generates compose, starts container, attaches
+build.rs # Sets RAMEKIN_VERSION from env or git rev
+assets/
+ Dockerfile # Agent container image (Node.js + pi + jj + Rust)
+ ramekin.ts # Pi extension injected into the container
+clippy.toml # Disallows std::fs in favor of fs-err
+justfile # Local dev tasks (check, fmt, clippy, test, install)
+.github/workflows/
+ ci.yml # CI: fmt + clippy + test
+ release.yml # CalVer release: build macOS binary + DotSlash
```
## Build and test
```sh
-cargo check --workspace # Type-check both crates
-cargo fmt --all # Format all code
-cargo clippy --workspace # Lint
-cargo test --workspace # Run tests (firewall crate has tests; CLI does not yet)
-```
-
-Or use `just` which runs check, fmt, and clippy together:
-
-```sh
-just
+cargo check # Type-check
+cargo fmt # Format
+cargo clippy # Lint
+cargo test # Run tests
+just # All four
```
## Conventions
-- **Rust edition 2024**, resolver v2 workspace.
+- Rust edition 2024, resolver v2 workspace.
- Error handling uses `color-eyre`. Prefer `wrap_err` / `bail!` over `.unwrap()`.
+- File I/O uses `fs-err` instead of `std::fs`. The clippy config enforces this.
- Logging uses `tracing` with `tracing-subscriber` and `EnvFilter`. Use `tracing::info`, `tracing::error`, etc. — not `println!` or `eprintln!`.
-- All CI checks must pass: `cargo fmt --all --check`, `cargo clippy --workspace`, `cargo test --workspace`.
-- The firewall's `entrypoint.sh` must not `flush ruleset` — it deletes and recreates only `table inet filter` to preserve Docker's iptables-nft NAT/DNS rules.
+- All CI checks must pass: `cargo fmt --all --check`, `cargo clippy --workspace -- -D warnings`, `cargo test --workspace`.
## Architecture notes
-- Docker compose config is generated at runtime, not a static file. The `generate_compose` function builds YAML based on firewall flag, Dockerfile path, and volume mounts.
-- With firewall enabled (default), the agent uses `network_mode: "service:firewall"` so all its traffic traverses the firewall's nftables rules. With `--no-firewall`, the agent runs with normal Docker networking.
-- XDG directories under the `ramekin` prefix store pi state: data in `XDG_DATA_HOME/ramekin`, config in `XDG_CONFIG_HOME/ramekin`.
-- BYOC: if the workspace contains `.ramekin/Dockerfile`, the CLI uses it as the agent image instead of the default.
-- The bridge server currently only has an `/echo` endpoint. It is not a proxy.
+- Docker compose config is generated at runtime, not a static file. The `generate_compose` function builds a YAML string from resolved paths and volume mounts.
+- XDG directories under the `ramekin` prefix store pi state: data in `$XDG_DATA_HOME/ramekin`, config in `$XDG_CONFIG_HOME/ramekin`.
+- Each workspace gets a per-repo sessions directory keyed by a `<dirname>-<hash>` slug.
+- If the workspace contains `.ramekin/Dockerfile`, the CLI builds it on top of `ramekin-agent` instead of using the base image directly.
+- The `ramekin.ts` extension is written into the agent config directory on every run. It injects container environment context into the pi system prompt.
+- Version is set at build time via the `RAMEKIN_VERSION` env var (used by CI) or falls back to `dev+<short-sha>`.
## Task management
diff --git a/README.md b/README.md
index ac0416f..d341fb5 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,9 @@ Containerized harness for running the [pi coding agent](https://github.com/badlo
## Quick start
```sh
-cargo run # run pi in a container against the current directory
-cargo run -- /some/path # mount a specific workspace
+ramekin # run pi against the current directory
+ramekin /some/path # mount a specific workspace
+ramekin run --rebuild # force a full image rebuild
```
Ramekin builds a Docker image with pi and its dependencies, starts it via Docker Compose, and attaches your terminal. Auth state, settings, and keybindings persist across runs.
@@ -16,20 +17,37 @@ Ramekin builds a Docker image with pi and its dependencies, starts it via Docker
A Rust CLI orchestrates a Docker Compose stack. On each run it:
1. Creates XDG directories for persistent state
-2. Writes the embedded Dockerfile and compose file to `$XDG_CACHE_HOME/ramekin/`
-3. Starts the agent container with the workspace mounted at `/workspace`
-4. Attaches interactively, then tears down on exit
+2. Writes the embedded Dockerfile to `$XDG_CACHE_HOME/ramekin/`
+3. Generates a compose config and writes it to a session-scoped cache directory
+4. Builds the agent image (and a project-specific layer, if one exists)
+5. Starts the agent container with the workspace mounted at `/workspace`
+6. Attaches interactively, then tears down on exit
+
+### Subcommands
+
+`run` (default) starts a containerized pi session. Pass `--rebuild` to ignore Docker layer cache and pull fresh base images.
+
+`config` prints resolved paths, volume mounts, and Dockerfile status without starting anything — useful for debugging mount issues.
### Persistence
-| What | Where (host) | Where (container) |
-|---|---|---|
-| Auth, sessions | `$XDG_DATA_HOME/ramekin/` | `/root/.pi` |
-| settings.json | `$XDG_CONFIG_HOME/ramekin/settings.json` | `/root/.pi/agent/settings.json` |
-| keybindings.json | `$XDG_CONFIG_HOME/ramekin/keybindings.json` | `/root/.pi/agent/keybindings.json` |
-| AGENTS.md | `$XDG_CONFIG_HOME/ramekin/AGENTS.md` | `/root/.pi/agent/AGENTS.md` |
+The agent directory (`$XDG_CONFIG_HOME/ramekin/agent/`) is mounted into the container at `/root/.pi/agent`. It holds:
+
+| File | Seeded as |
+|---|---|
+| settings.json | `{}` |
+| keybindings.json | `{}` |
+| AGENTS.md | empty |
-Settings and keybindings are seeded as empty JSON (`{}`) on first run. AGENTS.md is seeded empty.
+The full pi data directory (`$XDG_DATA_HOME/ramekin/`) is mounted at `/root/.pi` for auth tokens and session history. Each workspace also gets its own sessions directory under `$XDG_DATA_HOME/ramekin/repos/<slug>/sessions/`.
+
+Additional read-only mounts are added when the host directories exist:
+
+| Host path | Container path |
+|---|---|
+| `$XDG_CONFIG_HOME/git/` | `/root/.config/git` (read-only) |
+| `$XDG_CONFIG_HOME/jj/` | `/root/.config/jj` (read-only) |
+| `$XDG_DATA_HOME/ranger/` | `/root/.local/share/ranger` |
### Container environment extension
@@ -37,7 +55,9 @@ A built-in pi extension (`ramekin.ts`) is mounted into the agent container. It a
### Custom Dockerfile
-Place a `Dockerfile` at `.ramekin/Dockerfile` in your workspace to extend the base agent image. Use `FROM ramekin-agent` to layer on top — the base image includes Node.js, pi, git, ripgrep, and fd. The workspace is used as the build context, so `COPY` instructions work relative to the project root.
+Place a `Dockerfile` at `.ramekin/Dockerfile` in your workspace to extend the base agent image. Use `FROM ramekin-agent` to layer on top — the base image includes Node.js, pi, git, jj, ripgrep, fd, just, and Rust tooling.
+
+The workspace is used as the build context, so `COPY` instructions work relative to the project root.
```dockerfile
FROM ramekin-agent
@@ -50,5 +70,7 @@ RUN apt-get update && apt-get install -y ruby && rm -rf /var/lib/apt/lists/*
cargo check # type-check
cargo fmt # format
cargo clippy # lint
-just # all three
+cargo test # run tests
+just # all four
+just install # cargo install from local source
```