Skip to content

feat(caprock): add ocap proxy plugin#953

Draft
grypez wants to merge 34 commits into
grypez/bringing-in-the-sheavesfrom
grypez/caprock
Draft

feat(caprock): add ocap proxy plugin#953
grypez wants to merge 34 commits into
grypez/bringing-in-the-sheavesfrom
grypez/caprock

Conversation

@grypez
Copy link
Copy Markdown
Contributor

@grypez grypez commented May 26, 2026

WIP

grypez and others added 30 commits May 20, 2026 11:56
…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>
Adds @ocap/kernel-tui with an Ink/React terminal UI for interactive session
authorization. Adds the `ocap modal <sid>` command to kernel-cli that spawns
the TUI binary with the resolved channel URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ports the old ephemeral kernel-tui components (files, objects, invoke, log)
to work against the daemon via makeDaemonKernelApi, which calls kernel RPC
methods over the UNIX socket. Adds a sessions view that polls all sessions
and their pending authorization requests with keyboard approve/reject.
Adds `ocap-tui tui` command alongside the existing `modal` command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
exactOptionalPropertyTypes rejects color={undefined} on ink's Text component.
Use spread to conditionally apply focused styles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds `ocap tui` to kernel-cli for convenient TUI launch (mirrors `ocap modal`).
Sessions view now shows a helpful hint when the daemon lacks session RPCs
(i.e. is running from main rather than the sessions branch).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…uest from kernel-utils

Removes locally-defined SessionSummary, PendingRequest, and the explicit
session method declarations from KernelApi, replacing them with the shared
types from @metamask/kernel-utils/session. Drops the unused @metamask/utils
runtime dependency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add overflow=hidden to content box and cap object registry promises
  at 20 rows to prevent the view from overflowing the terminal
- Remove VIEW_LABELS title box from tui.tsx root — FileBrowser,
  InvokeView, and ObjectRegistryView already render their own headers,
  so the extra box was causing duplicate titles
- Add Sessions title to SessionsView to match the other views
- Clear screen before rendering and constrain root height to terminal
  rows via useTerminalSize to prevent rendering artifacts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- sessions-view: replace flat request list with per-session cursor;
  right arrow drills into a session's detail view; left arrow returns
- session-detail-view: new component showing session history entries
  with authorization status, timestamps, and descriptions
- use-kernel: add listHistory to KernelApi, expose SessionHistoryEntry
- types: expand listSessions return type to include cwd/startedAt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New requests arriving while inspecting a session were invisible until the
user navigated away and back. Add a POLL_INTERVAL_MS interval tied to
detailSession so refreshDetail runs automatically while the detail view
is active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ta hook

SessionsView previously owned polling intervals, all fetch callbacks, loading/
error state, and detail-open state alongside its render logic. Move all of that
into a useSessionData hook so the component owns only cursor position.

- hooks/use-session-data.ts: new hook — owns refresh/refreshDetail intervals,
  openDetail/closeDetail, onDecided, loading, error, sessions, detailHistory
- sessions-view.tsx: consumes useSessionData; only local state is cursor;
  extract sessionMetaSuffix helper to keep render logic readable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…SessionData hook

- session-registry.test.ts: 11 tests for listHistory(), authorizeRequest()
  (including timeout), createSession() with cwd/startedAt
- rpc-socket-server.test.ts: 9 integration tests for session.history,
  session.authorize, and updated session.create/list/get responses
- use-session-data.test.ts: 9 hook tests covering loading state, session
  fetch, error handling, openDetail, closeDetail, and polling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Bash commands

- Show most-recent history entries at the top of the detail view
- Expand entries by rendering prettified JSON with description(params) format
- Split compound shell commands on &&, |, ; into list segments for readability

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t/reject keybinds

- Replace numeric cursor index with token-based focus in SessionDetailView so
  new requests arriving above the cursor no longer shift what's highlighted
- Show oldest pending request expanded at the session list level; 1/3 decide
  without drilling into the detail view
- Change accept/reject keybinds from a/r to 1/3 throughout (status bar updated)
- Export formatExpandedContent and parseDescription for reuse across views

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migrates the standalone `pola` plugin into the monorepo as `@ocap/caprock`.
The plugin routes every Claude Code tool invocation through an ocap-kernel
permission vat (POLA enforcement), blocking for a TUI decision on first
use of any (tool, input-hash) pair not yet in the allow-set.

Key factoring decisions made visible by co-location:
- `sendCommand`/`getSocketPath` imported from `@metamask/kernel-node-runtime`
- `Decision` type imported from `@metamask/kernel-utils/session`
- `getOcapHome` imported from `@metamask/kernel-utils/nodejs`
- Vat bundle built at `build` time via `ocap bundle` (no committed artifact)

Includes 62 unit tests covering `decompose()` (bash parser security logic)
and `session.ts` persistence round-trips.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sorts candidates ascending by numeric `authority` metadata, so the most-
restricted matching section is tried first. Candidates without an authority
entry default to 0.5 (midpoint).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ArgPattern / InvocationPattern / Provision types and the full
algebra: interval builders (pathInterval, trivialInterval, argInterval),
matchArg / matchPattern / matchProvision, argPatternLe,
compareInvocationPatterns / compareProvisions, computeAuthority
(midpoint-embedding of the partial order into (0,1)), and
invocationToProvision.

Adds provision? to Decision and SessionApi.decide so approved provisions
can flow from the TUI through the RPC layer to the hook.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rewrites the permission-tracker vat to use the real sheaf machinery:
each Provision becomes a Provider<{authority,idx}> with an M.eq guard
for the tool name and an identity handler that throws on pattern
mismatch. leastAuthority drives the dispatch so the most-restricted
matching section wins.

The idx field in metadata prevents collapseEquivalent from merging
distinct providers that share the same authority value (incomparable
provisions).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the old vatCheck/vatGrant SHA-based API with invocation-aware
routing. buildInvocations parses Bash via decompose() and falls back to
[{name: tool, argv: stringFields}] for other tools. On a pre-tool-use
accept the hook records the approved provision (or constructs an exact
one via invocationToProvision). On post-tool-use it always records an
exact provision for the completed invocation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Passes the optional provision from the TUI's decide call through the
RPC socket layer to the kernel session, so the permission-tracker vat
can record whatever provision the user approved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hority

noopPolicy takes one argument; passing context caused a TS2554 build error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The hook process must not run SES lockdown because full lockdown
freezes native prototypes and breaks tree-sitter's C++ bindings.
@Endo modules call harden() and assert() at module-evaluation time,
so any transitive import of @endo/* would crash the hook with
'harden is not defined' or 'Cannot initialize @endo/errors'.

- Add harden-shim.ts: installs a no-op identity harden before any
  @Endo import can evaluate (ESM depth-first import order guarantees
  this runs first)
- Inline sendCommand/readLine/writeLine/connectSocket into rpc.ts
  using only node:crypto and node:net, removing the import of
  @metamask/kernel-node-runtime/daemon which transitively pulled in
  @endo/promise-kit and @endo/errors
- Add kernel-utils/session/provision lockdown-free subpath that
  re-exports only from types.ts and provision.ts (no channel.ts,
  no @endo/promise-kit); hook.ts imports from this subpath
- Remove @metamask/kernel-node-runtime from caprock dependencies
  and tsconfig references
- Fix hooks.json and scripts to use dist/bin/hook.mjs (ts-bridge
  with rootDir:. outputs bin/ under dist/bin/)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Carries parsed command invocations from the hook call-site through
the session registry and channel to TUI history entries, enabling
the provision editor to show per-arg pattern controls.

- Move ParsedInvocation type to session/types.ts to avoid circular deps
- Add invocations? to SectionNotification and SessionHistoryEntry
- Change authorizeRequest signature to an options bag
  { reason?, timeoutMs?, invocations? } for extensibility
- channel.listAll() includes invocations via ifDefined in both the
  pending and decided entry paths
- rpc-socket-server session.authorize handler extracts invocations
  from JSON-RPC params and forwards to session.authorizeRequest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a '2' keybind on pending session-detail entries that opens an
interactive provision editor before granting a standing provision.

The editor flattens all invocation args into a navigable list.
Each arg cycles through its pattern interval (exact → prefix levels
→ wildcard) via ↑/↓; ←/→ moves between args. Enter submits the
shaped Provision; Esc cancels back to the normal view.

Falls back to a tool-level wildcard provision when invocations data
is unavailable (unparseable command or old daemon).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
grypez and others added 4 commits May 23, 2026 16:02
Spawns dist/bin/hook.mjs as a child process and asserts it exits
cleanly without @endo/SES missing-globals errors. Builds the binary
in beforeAll so the test is always self-contained and never silently
skips due to a stale or missing dist/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a 'provisioned' status for auto-accepted requests and expose the
standing provision that was set by the user when accepting with keybind 2.

- `SessionHistoryEntry` gains status 'provisioned' and optional `provision` field
- `Channel.record()` inserts a pre-decided entry without blocking subscribers
- `Session.recordProvisioned()` records an auto-accepted request synchronously
- `session.record` RPC dispatches to `recordProvisioned` in the daemon
- hook fires-and-forgets `recordProvisioned` when the vat returns 'allow'
- TUI: ◆ for user-accepted-with-provision, → for auto-provisioned
- TUI: expanded view shows provision patterns; provisioned entries show '→ standing provision' instead of decided time

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…anel

Tweak 1 — show which provision auto-accepted a request:
- Add findMatch(tool, invocations) and listProvisions() to permission-tracker vat
- Hook calls vatFindMatch after vatRoute returns 'allow' and passes the result
  to recordProvisioned, threading it through rpc.ts → session.record RPC →
  channel.record() → HistoryEntry.provision
- TUI: provisioned entries show '→ by provision: git log * | head *' (compact
  one-liner) instead of '→ standing provision'; detailed pattern block reserved
  for user-accepted-with-provision (◆) entries

Tweak 2 — active-provisions panel (P keybind):
- ProvisionsPanel component derives unique active provisions from session history
- Press P in session detail view to open; Esc to close
- Lists each provision as '◆ tool  name arg1 arg2 | name2 arg1'
- Status bar hint updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@endo/marshal smallcaps encoding prefixes strings starting with sigil
characters (like `-` for negative floats) with `!`. Shell flags such as
`--oneline` or `-l` were rendered as `!--oneline` and `!-l` in the TUI
because vatFindMatch decoded only the outer CapData wrapper, leaving
nested strings un-unescaped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@grypez grypez changed the base branch from main to grypez/bringing-in-the-sheaves May 26, 2026 16:34
@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedink-spinner@​5.0.01001007682100
Addedink-select-input@​6.2.010010010082100
Addedink-text-input@​6.0.010010010082100
Addedtree-sitter-bash@​0.25.1881008687100
Addedink@​5.2.19810010096100

View full report

@socket-security
Copy link
Copy Markdown

Caution

MetaMask internal reviewing guidelines:

  • Do not ignore-all
  • Each alert has instructions on how to review if you don't know what it means. If lost, ask your Security Liaison or the supply-chain group
  • Copy-paste ignore lines for specific packages or a group of one kind with a note on what research you did to deem it safe.
    @SocketSecurity ignore npm/PACKAGE@VERSION
Action Severity Alert  (click "▶" to expand/collapse)
Block Medium
Network access: npm react-reconciler in module globalThis["fetch"]

Module: globalThis["fetch"]

Location: Package overview

From: ?npm/ink@5.2.1npm/react-reconciler@0.29.2

ℹ Read more on: This package | This alert | What is network access?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should remove all network access that is functionally unnecessary. Consumers should audit network access to ensure legitimate use.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/react-reconciler@0.29.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block Medium
Native binaries present: npm tree-sitter-bash

Location: Package overview

From: packages/caprock/package.jsonnpm/tree-sitter-bash@0.25.1

ℹ Read more on: This package | This alert | Why is native code a concern?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Verify that the inclusion of native code is expected and necessary for this package's functionality. If it is unnecessary or unexpected, consider using alternative packages without native code to mitigate potential risks.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/tree-sitter-bash@0.25.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block Medium
Network access: npm yoga-layout in module globalThis["fetch"]

Module: globalThis["fetch"]

Location: Package overview

From: ?npm/ink@5.2.1npm/yoga-layout@3.2.1

ℹ Read more on: This package | This alert | What is network access?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should remove all network access that is functionally unnecessary. Consumers should audit network access to ensure legitimate use.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/yoga-layout@3.2.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm node-addon-api is 100.0% likely to have a medium risk anomaly

Notes: The script is a legitimate formatting helper within a Node.js project. It orchestrates clang-format via git-clang-format, supports fix and diff modes, and provides actionable feedback to the developer. While operational dependencies exist, no malicious activity or data leakage is evident based on the provided code and typical usage.

Confidence: 1.00

Severity: 0.60

From: ?npm/tree-sitter@0.25.0npm/tree-sitter-javascript@0.25.0npm/tree-sitter-bash@0.25.1npm/node-addon-api@8.7.0

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/node-addon-api@8.7.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm ws is 100.0% likely to have a medium risk anomaly

Notes: The code implements a standard EventTarget-like mixin for wrapping event listeners and dispatching events to user callbacks. There are no suspicious patterns such as dynamic code execution, hardcoded secrets, or network activity. The risk is contingent on what the consumer does inside their handlers; the snippet itself does not introduce malware or data leakage mechanisms beyond normal event dispatch. Overall security risk is low in isolation.

Confidence: 1.00

Severity: 0.60

From: ?npm/@libp2p/websockets@10.1.7npm/@vitest/browser@4.1.3npm/ink@5.2.1npm/ws@8.20.1

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/ws@8.20.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm ws is 100.0% likely to have a medium risk anomaly

Notes: The analyzed code segment represents a robust, standards-aligned WebSocket receiver. It correctly handles frame parsing, masking, fragmentation, and optional compression via PerMessageDeflate, with appropriate validation and error signaling. There is no evidence of malicious intent or backdoors within this module; the security posture is solid for a protocol parser, with typical risks mitigated by payload size checks and UTF-8 validation. Overall, the code is appropriate for integration in a WebSocket client/server library, with moderate security risk primarily tied to how downstream consumers handle emitted data and potential resource usage under edge cases.

Confidence: 1.00

Severity: 0.60

From: ?npm/@libp2p/websockets@10.1.7npm/@vitest/browser@4.1.3npm/ink@5.2.1npm/ws@8.20.1

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/ws@8.20.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant