|
| 1 | +# Caprock plugin: code organization |
| 2 | + |
| 3 | +Reference for how `@ocap/caprock` is laid out and how its parts talk to each |
| 4 | +other. Descriptive of the current package; for forward-looking design see |
| 5 | +[`caprock-pipeline-rewrite-plan.md`](./caprock-pipeline-rewrite-plan.md). |
| 6 | + |
| 7 | +## What it is |
| 8 | + |
| 9 | +A Claude Code plugin (`@ocap/caprock`, private) that intercepts every Claude |
| 10 | +tool invocation via hooks, routes the structured invocation through a |
| 11 | +sheaf-backed permission vat running inside ocap-kernel, and returns |
| 12 | +allow/ask/deny. Per-session state and an append-only event log live in |
| 13 | +`~/.caprock/`. |
| 14 | + |
| 15 | +## Top-level layout |
| 16 | + |
| 17 | +``` |
| 18 | +packages/caprock/ |
| 19 | +├── .claude-plugin/plugin.json ← Claude Code plugin manifest (name, version) |
| 20 | +├── hooks/hooks.json ← one entry per hook event, all → bin/hook.mjs |
| 21 | +├── package.json ← name=@ocap/caprock; ships dist/ + vat/ + hooks/ + skills/ |
| 22 | +├── bin/ ← CLI entrypoints (compiled to dist/bin/*.mjs) |
| 23 | +├── src/ ← library code (compiled to dist/index.mjs) |
| 24 | +├── vat/ ← in-kernel vat code (bundled to .bundle, committed) |
| 25 | +├── scripts/ ← thin shell wrappers around bin/ for user-facing CLIs |
| 26 | +├── skills/{audit,setup,status}/SKILL.md ← slash-command surfaces |
| 27 | +└── docs/ ← this file, and the rewrite plan |
| 28 | +``` |
| 29 | + |
| 30 | +## The three layers |
| 31 | + |
| 32 | +### 1. Hook layer — `bin/` |
| 33 | + |
| 34 | +Single entrypoint `hook.ts` wired to every Claude hook event by |
| 35 | +`hooks/hooks.json`. Reads JSON payload from stdin, dispatches to one of: |
| 36 | +`onSessionStart`, `onPreToolUse`, `onPostToolUse`, `onPermissionRequest`, etc. |
| 37 | + |
| 38 | +Per-event responsibilities: |
| 39 | + |
| 40 | +- **SessionStart** — boot session: `ensureDaemon()`, `launchPermissionVat()`, |
| 41 | + `createKernelSession()`, persist `SessionState`. |
| 42 | +- **PreToolUse** — `buildClauses(tool_name, tool_input)` → for Bash, |
| 43 | + `decompose()` from `src/bash.ts`; for other tools, wrap as one clause. Route |
| 44 | + each clause via `vatRoute()`. If allow → continue; else → `authorizeRequest()` |
| 45 | + (kernel surfaces a TUI prompt). |
| 46 | +- **PostToolUse / PermissionDenied / FileChanged** — record events, refresh |
| 47 | + provisioned list. |
| 48 | +- **SessionEnd** — write summary, tear down kernel session. |
| 49 | + |
| 50 | +Other `bin/` tools: |
| 51 | + |
| 52 | +- `setup.ts` — installer health checks (tree-sitter native binding, kernel |
| 53 | + daemon). |
| 54 | +- `status.ts` — pretty-print current session state. |
| 55 | +- `audit.ts` — replay a transcript against current rules; the only file with |
| 56 | + regex (used to match Claude Code permission _globs_, not bash syntax). |
| 57 | +- `harden-shim.ts` — minimal shim because `@endo` lockdown is incompatible with |
| 58 | + the native tree-sitter binding. |
| 59 | + |
| 60 | +### 2. RPC / state layer — `src/` |
| 61 | + |
| 62 | +Library code consumed by `bin/` (and exported via `src/index.ts` for |
| 63 | +embedding). |
| 64 | + |
| 65 | +- **`bash.ts`** — the AST core. `decompose(source) → DecomposeResult` parses |
| 66 | + with tree-sitter-bash, dispatches via `SAFETY_FRAGMENT` (a table of |
| 67 | + recognized AST node kinds → handler), returns clauses or refuses with a |
| 68 | + named `DropReason`. Plus security checks (`hasCurlPipeShell`, |
| 69 | + `hasEvalDynamic`). This is the single entry point for bash understanding in |
| 70 | + the whole package. |
| 71 | +- **`rpc.ts`** — minimal JSON-RPC client over the kernel's UNIX socket (no |
| 72 | + `@endo` deps — keeps the hook small and lockdown-free). Exports |
| 73 | + `sendCommand`, `createKernelSession`, `authorizeRequest`, |
| 74 | + `recordProvisioned`. The kernel side is the ocap-kernel daemon. |
| 75 | +- **`session.ts`** — pure persistence: load/save `SessionState`, append events |
| 76 | + to `<session-id>.jsonl`, read settings allow/deny lists from |
| 77 | + `.claude/settings.json`. |
| 78 | +- **`transcript.ts`** — parse Claude Code transcript JSONL (used by audit). |
| 79 | +- **`types.ts`** — `SessionState`, `CaprockEvent` / `CaprockEventKind`, hook |
| 80 | + payload types (`PreToolUsePayload`, etc.). |
| 81 | +- **`paths/`** — three small modules resolving filesystem locations: `user.ts` |
| 82 | + (HOME), `plugin.ts` (plugin install root), `ocap-kernel.ts` (caprock data |
| 83 | + dir, kernel binary path). |
| 84 | +- **`index.ts`** — three re-exports (`types`, `session`, `rpc`). Tiny. |
| 85 | + |
| 86 | +### 3. In-vat layer — `vat/` |
| 87 | + |
| 88 | +`permission-tracker.ts` (plus the committed `.bundle` artifact). This is the |
| 89 | +part that _runs inside the kernel_, not in the hook process. |
| 90 | + |
| 91 | +- Built into a vat bundle by `yarn build` (the build script also bundles). |
| 92 | +- Per Claude session, the hook launches one of these vats. It maintains a |
| 93 | + sheaf of `Provider<Meta>` capabilities — one per `Provision` (a permission |
| 94 | + grant). |
| 95 | +- Methods: `route(tool, invocations)` (the dispatch), `addSection(provision)` |
| 96 | + (grant), `findMatch`, `listProvisions`, `size`. |
| 97 | +- Uses `@metamask/sheaves` (`sheafify`, `leastAuthority`, `makeHandler`) — the |
| 98 | + actual sheaf machinery lives there, this is just the per-permission |
| 99 | + encoding. |
| 100 | +- Uses `@metamask/kernel-utils/session` for `computeAuthority`, `matchPattern`, |
| 101 | + `matchProvision`, the `Provision` type — the shared parser/authority model. |
| 102 | + This package only provides the _vat-side_ of it. |
| 103 | + |
| 104 | +## Communication boundaries |
| 105 | + |
| 106 | +``` |
| 107 | +Claude Code ocap-kernel daemon |
| 108 | +───────────── ────────────────── |
| 109 | +fires hook |
| 110 | + ↓ |
| 111 | +bin/hook.ts (subprocess) |
| 112 | + uses src/bash.ts ───────┐ each session → |
| 113 | + uses src/session.ts │ ┌──────────────────────┐ |
| 114 | + uses src/rpc.ts ───── UNIX socket → permission-tracker vat |
| 115 | + writes ~/.caprock/ │ │ (vat/permission-tracker.ts) |
| 116 | + reads ~/.claude/ │ │ sheaf of Provisions |
| 117 | + │ └──────────────────────┘ |
| 118 | +returns allow/ask/deny ◄──┘ |
| 119 | +``` |
| 120 | + |
| 121 | +The hook process is short-lived, runs once per event, holds no shared state. |
| 122 | +All durable state is either on disk (`~/.caprock/`) or in the kernel daemon's |
| 123 | +vat. The hook never imports anything from `vat/` directly — the only contact |
| 124 | +is via JSON-RPC over the socket. |
| 125 | + |
| 126 | +## The bash layer's primacy |
| 127 | + |
| 128 | +Bash understanding is funneled through `src/bash.ts` from a single entry point |
| 129 | +(`decompose()`), gated by a positive `SAFETY_FRAGMENT` so unknown AST shapes |
| 130 | +refuse with `unsupported_construct` rather than fall through to a permissive |
| 131 | +walk. Extending the recognized set is a one-line `SAFETY_FRAGMENT` entry plus |
| 132 | +tests — a deliberate decision per AST node kind, not an accident of "it |
| 133 | +happened to parse." This is the foundation the planned rewriter (per-stage |
| 134 | +`caprock` wrapping, per |
| 135 | +[`caprock-pipeline-rewrite-plan.md`](./caprock-pipeline-rewrite-plan.md)) will |
| 136 | +sit on top of. |
| 137 | + |
| 138 | +## Skills + scripts (user-facing CLIs) |
| 139 | + |
| 140 | +`skills/{audit,setup,status}/SKILL.md` are Claude slash-command definitions. |
| 141 | +Each delegates to a thin shell wrapper in `scripts/` (e.g., `scripts/status.sh` |
| 142 | +→ `node dist/bin/status.mjs`). The split lets the underlying TS get |
| 143 | +type-checked and bundled while the surface remains shell-callable. |
| 144 | + |
| 145 | +## What lives _outside_ this package |
| 146 | + |
| 147 | +Worth knowing where the boundaries are: |
| 148 | + |
| 149 | +- The sheaf algebra (`sheafify`, `leastAuthority`, sections, lifts) — |
| 150 | + `@metamask/sheaves`. |
| 151 | +- The `Provision` data model, `computeAuthority`, pattern matching — |
| 152 | + `@metamask/kernel-utils/session`. |
| 153 | +- The kernel daemon, vat lifecycle, TUI prompting — the broader ocap-kernel. |
| 154 | +- Tree-sitter parser — `tree-sitter` + `tree-sitter-bash` (native bindings; |
| 155 | + needs the `harden-shim` workaround). |
| 156 | + |
| 157 | +Caprock itself is mostly _glue and policy_: parse the bash, plumb the RPC, |
| 158 | +persist the session, and host one specific vat that encodes "this permission |
| 159 | +grant means this routing rule." |
0 commit comments