Skip to content

Conversation

@rekmarks
Copy link
Member

@rekmarks rekmarks commented Jan 14, 2026

Introduces "controllers" to Omnium. A controller is an object (class) responsible for a slice of application state. We inherit this nomenclature from MetaMask.

In detail:

  • Add modular controller architecture with POLA attenuation via makeFacet()
    • makeFacet() is tentative, and may be removed later in the stack in favor of just public and private methods.
  • Add ControllerStorage abstraction with immer-based state management and debounced persistence
  • Add platform storage adapters and Chrome-specific implementation
  • Add Controller abstract base class
  • Add CapletController for managing installed caplets

Part 2 of 4 in PR stack
Depends on: #751
Followed by: #753


Note

Introduces a modular controller layer and integrates it into the extension background, enabling managed, persisted state and caplet lifecycle operations.

  • Controller framework: Adds abstract Controller, ControllerStorage (immer-based with debounced, per-key persistence), and a Chrome storage adapter
  • Caplet management: New CapletController with install/uninstall/list/get, manifest validation, and kernel subcluster launch/terminate via initializeControllers
  • Background integration: Refactors globals to omnium (E, getKernel, ping) and exposes omnium.caplet methods; adds error propagation to offscreen stream
  • Types/utilities: Exports Promisified type; caplet types/guards with superstruct and semver
  • Config/tests: Adds deps (exo, superstruct, immer, semver), grants storage permission in manifest, and comprehensive unit/e2e tests

Written by Cursor Bugbot for commit 0e330b6. This will update automatically on new commits. Configure here.

@socket-security
Copy link

socket-security bot commented Jan 14, 2026

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

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​types/​semver@​7.7.0 ⏵ 7.7.1100 +110075 +181100
Addedimmer@​10.2.0991008393100

View full report

@rekmarks rekmarks marked this pull request as draft January 14, 2026 23:26
@rekmarks rekmarks force-pushed the rekm/controller-architecture branch 2 times, most recently from 6736065 to 89e5113 Compare January 15, 2026 00:00
@rekmarks rekmarks force-pushed the rekm/controller-architecture branch from 89e5113 to ee93a06 Compare January 15, 2026 06:31
github-merge-queue bot pushed a commit that referenced this pull request Jan 20, 2026
Answers @kumavis's challenge of "What're you afraid of CapTP or
something?" by replacing our kernel JSON-RPC API with `E()` on a facet
of the kernel. This makes it easy to expose as much of the kernel API as
we want via eventual send, and allows us to benefit from pipelining
internally. In addition, it facilitates the removal of the command
stream and the related RPC API logic. Finally, a number of
rationalizations are applied to the extension and omnium.

This PR is part of a stack followed by: #752, #753, and #754



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces CapTP-based communication and removes the JSON-RPC command
stream.
> 
> - Add CapTP utilities in `kernel-browser-runtime`:
`makeBackgroundCapTP`, `makeKernelCapTP`, `makeKernelFacade`, and
JSON-RPC `captp` notification helpers; export new `KernelFacade` and
`CapTPMessage` types
> - Update `kernel-worker` to use CapTP for background↔kernel messaging;
initialize kernel without a command stream; route internal RPC only for
panel/internal comms
> - Refactor `@MetaMask/ocap-kernel` to drop command stream handling and
kernel RPC entrypoints; `Kernel.make` now accepts only platform services
and the database; adjust `stop()` accordingly
> - Migrate extension and omnium backgrounds/offscreens to CapTP over
`ChromeRuntimeDuplexStream`, use global `E` and `kernel`/`omnium`
helpers; remove `env/dev-console` and background trusted prelude files;
add TS globals
> - Add unit and integration tests for CapTP (`background-captp`,
kernel-side CapTP, and E() end-to-end); introduce
`vitest.integration.config.ts` and `test:integration` scripts; CI gains
an "Integration Tests" job
> - Update deps (add `@endo/captp`, `@endo/eventual-send`), tsconfigs,
build constants, and minor coverage thresholds
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cbf22fd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Base automatically changed from rekm/captp-infrastructure to main January 20, 2026 19:31
rekmarks and others added 8 commits January 20, 2026 12:47
Implement Phase 1.2 of the omnium plan - Define Caplet Structure:

- Add modular controller architecture with POLA attenuation via makeFacet()
- Add storage abstraction layer (StorageAdapter, NamespacedStorage)
- Add Chrome storage adapter for platform storage
- Add CapletController for managing installed caplets
- Add Caplet types with superstruct validation
- Wire CapletController into background.ts and expose on globalThis.omnium.caplet
- Add comprehensive unit tests for all controller code
- Update PLAN.md to reflect implementation
Consolidate CapletControllerState from multiple top-level keys
(installed, manifests, subclusters, installedAt) into a single
`caplets: Record<CapletId, InstalledCaplet>` structure.

Changes:
- Add ControllerStorage abstraction using Immer for state management
- Controllers work with typed state object instead of storage keys
- Only modified top-level keys are persisted (via Immer patches)
- Remove state corruption checks (no longer possible with atomic storage)
- Fix makeFacet type - use string | symbol instead of keyof MethodGuard
- Update PLAN.md to reflect new storage architecture

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add abstract Controller class with state management via ControllerStorage
- Convert CapletController to extend Controller base class
- Use makeFacet() pattern for returning hardened exo methods
- Add base-controller tests (12 tests)
- Add semver deep import type declaration
- Add storage permission to manifest.json

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Convert ControllerStorage from factory to class with static make() method
- Implement synchronous update() with debounced fire-and-forget persistence
- Fix critical debounce bug: accumulate modified keys across debounce window
- Implement bounded latency (timer not reset, max delay = one debounce interval)
- Add immediate writes when idle > debounceMs for better responsiveness
- Add clear() and clearState() methods to reset storage to defaults
- Remove old namespaced-storage implementation
- Refactor all tests to use actual ControllerStorage with mock adapters
- Add shared makeMockStorageAdapter() utility in test/utils.ts
- Update controllers to create their own storage from adapters

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove strict reverse DNS format requirement for CapletId to allow more flexibility during early development. Now accepts any non-empty ASCII string without whitespace, removing restrictions on hyphens, underscores, uppercase, and segment count.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@rekmarks rekmarks force-pushed the rekm/controller-architecture branch from d014ffd to 1ab49c9 Compare January 20, 2026 22:09
@rekmarks rekmarks marked this pull request as ready for review January 21, 2026 17:42
@rekmarks rekmarks marked this pull request as draft January 21, 2026 17:47
cursor[bot]

This comment was marked as outdated.

rekmarks and others added 5 commits January 21, 2026 10:57
- Add afterEach to restore real timers in caplet-controller tests
- Use fake timers consistently in controller-storage tests for deterministic debounce testing
- Replace setTimeout patterns with vi.runAllTimersAsync() for non-flaky tests
- Add error handling tests for Chrome Storage adapter (get, set, delete, keys)
- Add concurrent uninstall test to verify race condition handling
- Document that facet methods are async for E() compatibility
- Document Chrome Storage keys() limitation (loads all data into memory)

Co-Authored-By: Claude <noreply@anthropic.com>
}> {
const storageAdapter = makeChromeStorageAdapter();

const capletController = await CapletController.make(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caplet controller construction is simplified later in the stack.

Comment on lines +130 to +136
/**
* Returns the hardened exo with public methods.
* Subclasses implement this to define their public interface.
*
* @returns A hardened exo object with the controller's public methods.
*/
abstract makeFacet(): Methods;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm probably going to get rid of this conceit in a follow-up PR, but would prefer to avoid for now to not complicate merge train.

logger.injectStream(stream as unknown as DuplexStream<LogMessage>);

await waitUntilQuiescent(1000);
await waitUntilQuiescent(10);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incidental change after the root test:dev was failing due to these timeouts being needlessly long.

rekmarks and others added 2 commits January 21, 2026 18:52
- Replace test data constants with factory functions for better isolation
- Make logger factory recreate fresh mocks for each test
- Make manifest factory provide consistent test data
- Remove facet.ts and facet.test.ts - inline facet logic into controllers
- Simplify controller storage API and type definitions
- Remove unnecessary storage adapter abstractions
- Reduce controller-storage test complexity
- Consolidate controller tests with better setup patterns
- Update caplet controller to use simplified storage API

Co-Authored-By: Claude <noreply@anthropic.com>
@rekmarks rekmarks marked this pull request as ready for review January 22, 2026 03:08
cursor[bot]

This comment was marked as outdated.

@rekmarks
Copy link
Member Author

rekmarks commented Jan 22, 2026

CI currently failing due to an npm outage.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 88.2%
⬇️ -0.17%
5690 / 6451
🔵 Statements 88.08%
⬇️ -0.16%
5782 / 6564
🔵 Functions 87.18%
⬇️ -0.32%
1483 / 1701
🔵 Branches 84.49%
⬆️ +0.06%
2055 / 2432
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/extension/src/background.ts 0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
18-191
packages/kernel-utils/src/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-utils/src/types.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/omnium-gatherum/src/background.ts 0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
23-204
packages/omnium-gatherum/src/global.d.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/omnium-gatherum/src/controllers/base-controller.ts 88.88% 100% 83.33% 88.88% 88
packages/omnium-gatherum/src/controllers/index.ts 0% 0% 0% 0% 66-107
packages/omnium-gatherum/src/controllers/types.ts 100% 100% 100% 100%
packages/omnium-gatherum/src/controllers/caplet/caplet-controller.ts 100% 100% 100% 100%
packages/omnium-gatherum/src/controllers/caplet/index.ts 100% 100% 100% 100%
packages/omnium-gatherum/src/controllers/caplet/types.ts 100% 100% 100% 100%
packages/omnium-gatherum/src/controllers/storage/chrome-storage.ts 100% 100% 100% 100%
packages/omnium-gatherum/src/controllers/storage/controller-storage.ts 100% 87.5% 100% 100%
packages/omnium-gatherum/src/controllers/storage/index.ts 100% 100% 100% 100%
packages/omnium-gatherum/src/controllers/storage/types.ts 100% 100% 100% 100%
packages/omnium-gatherum/src/types/semver.d.ts 100% 100% 100% 100%
Generated in workflow #3282 for commit 0e330b6 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

@rekmarks rekmarks added this pull request to the merge queue Jan 22, 2026
Merged via the queue into main with commit fe8fb66 Jan 22, 2026
29 checks passed
@rekmarks rekmarks deleted the rekm/controller-architecture branch January 22, 2026 19:08
github-merge-queue bot pushed a commit that referenced this pull request Jan 23, 2026
Adds an actual caplet implementation, the echo caplet, and enables
installing it and retrieving a reference to its root object from the
extension's background console. The returned object is not actually a
usable presence, but we implement this in #754.

In detail:
- Adds echo caplet
- Store and retrieve caplet root krefs from the kernel API
- Add caplet manifest loading and installation
- Return subcluster id, root kref, and bootstrap result from
`launchSubcluster()`
- Add `omnium.caplet.load()` for dynamic caplet loading

**Part 3 of 4 in PR stack**
Depends on: #752
Followed by: #754 



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces caplet plumbing and updates kernel launch semantics to
return identifiers needed to reference caplet roots.
> 
> - **Kernel API**: `launchSubcluster()` now returns
`SubclusterLaunchResult` `{ subclusterId, bootstrapRootKref,
bootstrapResult }`; `SubclusterManager` and `Kernel` updated; types
exported
> - **Kernel browser facade/CapTP**: facade `launchSubcluster` now
returns `{ subclusterId, rootKref }`; added `getVatRoot(krefString)`;
CapTP/RPC handler/spec updated to return structured result and normalize
`bootstrapResult` undefined→null
> - **Omnium (extension)**: added **Echo caplet** (bundle + manifest)
and static copy in build; background exposes `omnium.caplet.load()`;
CapletController now stores `rootKref`, supports `getCapletRoot()`, and
uses kernel facade `launchSubcluster`/`getVatRoot`; integration tests
added
> - **Tests/fixtures**: updated across kernel, browser-runtime, node
helpers, and persistence/tests to consume new launch result and root
krefs
> - **Tooling**: ESLint allows caplet JS under `src/**/caplets`; build
script bundles caplets
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
bc6e395. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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.

3 participants