feat(caprock): add ocap proxy plugin#953
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Caution MetaMask internal reviewing guidelines:
|
ba12dbe to
01db469
Compare
…ives Adds two new capabilities needed by the modal authorization flow: NodeSocketDuplexStream (@metamask/streams): A duplex stream over a Node net.Socket. Reads NDJSON lines inbound, writes NDJSON lines outbound. Reader/writer cross-terminate on end. Exported via the streams package barrel. Session channel (kernel-utils/session): makeChannel() — a broadcast channel that fans SectionNotification messages to all connected ModalStream subscribers and resolves a Decision promise back to the broadcaster. New subscribers receive a replay of all currently-pending (undecided) notifications. SectionRequest / SectionNotification / Decision wire types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onApi types Transport-agnostic user-facing types used by both the TUI and the browser extension Authorization panel. Placing them in kernel-utils/session makes them available to any package without a node-runtime dependency. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ket server Adds ChannelFactory exo (kernel service), SessionRegistry, StreamSocketServer, and DaemonClient to support CLI-driven authorization session management. The daemon now exposes session RPC methods and a persistent stream socket for TUI subscriber connections. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- channel: track queuedAt timestamp, record history log with listAll() - session-registry: add startedAt/cwd, add listHistory() and authorizeRequest() - rpc-socket-server: add session.history and session.authorize RPC methods; include cwd/startedAt in session.create/list/get responses Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ocap session create/list/requests/queue/approve/reject subcommands. Refactors daemon-client to delegate socket/RPC helpers to kernel-node-runtime, and extracts session command builder into session.ts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… timeline entries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…erators Split bash commands joined by &&/||/; into independent clauses, each routed separately through the permission sheaf. A compound command is auto-accepted only when every clause has a covering provision. The TUI provision editor shows one pattern-tuning block per clause and creates one Provision per clause on submit. Migrates provision?:Provision → provisions?:Provision[] throughout the session layer (Decision, SessionHistoryEntry, Channel.record, etc.). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ropertyTypes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…decomposition Pipeline stages wrapped in a redirected_statement (e.g. `cmd 2>&1 | tail -30`) were silently dropped, causing only the tail end of the pipeline to appear as an invocation. A provision on `tail *` would then incorrectly auto-accept the entire command. Now all stages are collected regardless of redirect wrappers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends path detection to bare relative forms like `src/foo.ts` and `app/scripts/foo.ts` (word-char-prefixed segments containing a separator), not just absolute or `./`/`../`-prefixed paths. Provision routing was treating these as non-path args, which mis-matched commands that take relative project paths as the natural argument shape. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two observability additions to the caprock hook, prerequisite to the audit CLI but independently useful for transcript-driven inspection. - Reads `permissions.deny` from each watched settings file (in addition to the existing allow list) and captures it on the session state as `settingsDenySnapshot`, so the at-start view of authority is complete. - Records a `provision_match` event in the session log whenever a PreToolUse routing succeeds, naming the matched provisions. This makes "which provision authorized this tool use" inspectable from the event stream rather than only from the in-vat ledger. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds an `audit` CLI that reports which permission rules and provisions
were exercised in a given Claude session, plus SKILL.md manifests so
`audit`, `setup`, and `status` are reachable as slash commands from
Claude Code.
- `bin/audit.ts` + `scripts/audit.sh`: cross-references the transcript's
tool uses against the session-state allow/deny lists and the in-vat
provision ledger (via the new `listVatProvisions` RPC), and prints a
per-tool breakdown of which standing provisions were activated.
- `skills/{audit,setup,status}/SKILL.md`: thin wrappers that invoke the
corresponding `dist/bin/*.mjs` entry points. `setup` and `status`
existed as scripts; this is the slash-command surface.
- `src/rpc.ts:listVatProvisions`: queries the permission-tracker vat for
its current provisions list, decoded back to JS values.
- `package.json`: includes `skills/` in published files so the plugin
install ships the manifests.
- `bin/hook.ts:registerSkillPermissions`: adds the `audit.sh` entry to
the auto-registered Bash permissions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lift collectClauses into a SAFETY_FRAGMENT map (node kind → handler) so unknown node types refuse with unsupported_construct + node-kind detail instead of falling through to a permissive walk. Adds subshell and compound_statement as transparent grouping; moves hasCurlPipeShell ahead of clause collection so the security deny still fires when nested in an unsupported construct. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Descriptive overview of how the plugin is laid out: the hook subprocess, the src library (with bash.ts as the AST entry point), the in-kernel permission-tracker vat, and the communication boundary between them. Complements the forward-looking pipeline-rewrite-plan. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Define structs in `src/structs.ts` for the daemon responses caprock consumes (Provision, Decision, kernel session, launchSubcluster) plus a `Verdict = 'allow' | 'ask'` literal type, and use `superstruct.assert` at every `response.result` access in `src/rpc.ts`. The vat operations (`vatRoute`, `vatAddSection`, `vatFindMatch`, `vatSize`, `launchPermissionVat`) move from `bin/hook.ts` into `src/rpc.ts` so the whole RPC surface lives in one validated place, and `RpcClient` / `defaultRpcClient` are exported as the injection seam for handlers. `vatRoute` is now typed `Promise<Verdict>` instead of `Promise<string>`, so misshapen daemon responses fail loudly rather than silently flowing through as bogus strings.
`bin/audit.ts` and `bin/hook.ts` each had their own copy of "turn a tool invocation into clause arrays" — identical apart from the input type they accepted. Pull both into `src/clauses.ts` as one `buildClauses` that takes `(toolName, toolInput)`, plus a `routeAllClauses` helper for the cosheaf semantics (all clauses must independently match) and the small `inputSha` event-log key derivation. The audit binary switches to the shared helper here. The hook keeps its inline duplicate for now; that goes away in the handlers split.
…ed deps Each PreToolUse/PostToolUse/Permission*/FileChanged/SessionStart/SessionEnd handler moves into its own file under `src/handlers/`, taking a `HookDeps` bag (RPC client, session store, clock, stdout/stderr sinks, startup helpers) instead of reaching into module globals. `bin/hook.ts` becomes a thin shim: read stdin, build the production deps bag, hand off to `dispatch()`. The duplicated session-init paths in the old `onSessionStart` and `getOrInitSession` collapse into a single `initFreshSession`. The hook's inline `buildClauses` and `inputSha` are replaced with the shared `src/clauses.ts` exports from the prior commit. Decision flow is now unit-testable in milliseconds via the `HookDeps` fakes in `test/handler-fakes.ts` — no process spawning. The existing `bin/hook.test.ts` smoke test stays as the SES-lockdown regression check.
The daemon's `session.create` response includes `cwd?` and `startedAt`, and `launchSubcluster` includes `bootstrapResult` — fields the strict `KernelSessionStruct` and `LaunchSubclusterStruct` rejected with `Expected a value of type \`never\``. SessionStart threw inside the dispatcher, never persisted state, and every later PermissionRequest hook bailed at the missing-state check — leaving Claude Code's native permission prompt to handle everything. Add the missing fields as `optional` so the validators tolerate the real response shape. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The list rendered in daemon insertion order, which on long-lived daemons put the active session at the bottom and pushed it off-screen. Sort by `startedAt` descending so the most recent session is always visible; sessions without `startedAt` sort last. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tree-sitter-bash parses `a && b 2>&1 | tail` as `pipeline → [redirected_statement → list (a, b), file_redirect], command (tail)` — the && chain is pulled inside the pipeline's first stage. The walker treated the redirected_statement as a single pipeline stage and silently dropped the list, so a compound command like `cd /tmp && yarn build 2>&1 | tail -40` decomposed to just `tail -40` and the provision editor only let the user widen `tail`. When a pipeline's stage is a `redirected_statement → list`, lift the non-final list commands out as their own clauses (matching bash's actual `a && (b 2>&1 | tail)` precedence) and keep the final list command — with the redirect attached — as the pipeline's first stage. Same fix at the top level: `cmd1 && cmd2 2>&1` (no pipeline) now splits into two clauses with the redirect bound to cmd2 only. Tests cover the regression command, the n-ary && chain (`a && b && c 2>&1 | tail`), and the no-pipeline variant. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a `removeSection(provision)` method to the permission-tracker vat and wires it through to the active-provisions panel in the kernel TUI. The panel is now arrow-key traversable; pressing `3` opens an inline yes/no confirmation, and a confirmed revoke drops the section from the vat and appends a `provision_revoke` event to the caprock event log (consumed by `caprock:audit`). Two fixes uncovered along the way: - `kernel-tui` was sending `queueMessage` params as an object, but the daemon's spec is a positional tuple `[KRef, string, Json[]]`. The `send()` helper now forwards array params unchanged, so both `revoke` and the existing invoke view talk to the vat correctly. - The active-provisions list deduplicated by `JSON.stringify`, which treated key-reordered provisions (auto-provisioned entries arrive via the kernel CapData round-trip and may differ in key order) as distinct. Switched to a canonical `provisionKey` so each logical provision appears once. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Session state now stamps the hook binary version (from plugin.json) and the permission-tracker vat's baked-in version, and emits a version_up event whenever the hook is upgraded mid-session. A downgrade aborts the hook with a monotonic-versioning error. Sessions created before this change leave both fields undefined — no retroactive backfill — so a reader can confine analysis to a known version range. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The five vat-facing RPC helpers (routeAllClauses, vatRoute, vatAddSection, vatFindMatch, vatRemoveSection) and the matching RpcClient slots used positional parameters with three to five args each. CLAUDE.md mandates an options bag past two args, and because the shapes are typed on RpcClient, the violation propagated to every caprock handler call site. Convert all five to options bags with a consistent ordering: rpc first (only for routeAllClauses), then the connection pair (socketPath, rootKref), then the per-call payload. Updates every caller in src/handlers/ and the tests in clauses.test.ts. The fake RpcClient in test/handler-fakes.ts needs no change because vi.fn is structurally compatible with the new signatures. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The PreToolUse hook fell through to `continue: true` when the loaded session state was missing `kernelSessionId` — a state reachable when the daemon was down at session init, or when an older-format state file was loaded while the daemon was still unreachable. That was a silent security bypass for a degraded state. Deny with a hint that points the user at `/caprock:setup` to diagnose (tree-sitter bindings, daemon connectivity, permissions), and notes that subsequent tool uses will keep failing until the kernel session is created. Adds a test covering the branch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…s with superstruct The session.* RPC dispatch in the daemon validated params with hand-rolled typeof / Array.isArray checks and unsafe `as` casts. Only the array-ness of invocations / clauses / provisions was checked — per-entry shape was not — and those values flowed into the permission-tracker vat without further validation. Promote ParsedInvocationStruct, ArgPatternStruct, InvocationPatternStruct, ProvisionStruct, GuardStruct, and DecisionStruct into @metamask/kernel-utils/session, exposed via both the package index and the lockdown-free session/provision subpath so caprock can later drop its duplicated copies. Rewrite handleSessionRequest to assert per-method param structs via a small parseParams helper that surfaces StructError as SessionRpcError(-32602). Adds ten test cases covering rejection of malformed inputs and the happy paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The session-detail-view component file was 1235 lines, mixing the
top-level component, three subcomponents, pure formatting helpers,
layout math, and provision derivation. Split into a directory:
session-detail-view/
index.tsx (barrel)
session-detail-view.tsx (top-level component)
format.ts (pure formatting helpers)
layout.ts (entryRowCount, windowEndIdx, clampScroll)
provisions.ts (buildProvisions, provisionKey, etc.)
provision-editor.tsx
provisions-panel.tsx
revoke-confirm.tsx
Layout-math and buildProvisions switched to options-bag signatures
along the way (CLAUDE.md compliance past two args). Adds 62 vitest
cases for the pure helpers (parseDescription, splitShellCommand,
formatExpandedContent, layout math, provision derivation). Adds
@ocap/repo-tools/test-utils/mock-endoify to the package's vitest
setupFiles so tests can import from @metamask/kernel-utils/session,
matching kernel-utils's own setup.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
WIP