Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2d20e96
feat(ocap-kernel): Add KernelFacet
rekmarks Feb 2, 2026
7c314b3
feat(omnium-gatherum): Add system vats support
rekmarks Feb 3, 2026
10cc18e
refactor(omnium-gatherum): Convert bootstrap-vat to TypeScript
rekmarks Feb 3, 2026
0f20610
feat(kernel-browser-runtime): Add reset method to KernelFacade
rekmarks Feb 3, 2026
2e9f67b
refactor(omnium-gatherum): Rename bootstrap-vat to controller-vat
rekmarks Feb 3, 2026
8e086d0
fix(ocap-kernel): Avoid deadlock in E(kernelFacet) calls from within …
rekmarks Feb 3, 2026
54e9e99
feat(ocap-kernel): Add globals config for vat compartment endowments
rekmarks Feb 3, 2026
7a9c06c
feat(omnium-gatherum): Add callCapletMethod API and simplify echo caplet
rekmarks Feb 3, 2026
161d5de
chore(omnium-gatherum): Remove dead code from controller migration
rekmarks Feb 3, 2026
3ead152
fix(omnium-gatherum): Fix callBootstrap type annotations
rekmarks Feb 3, 2026
35737d9
fix(omnium-gatherum): Address review feedback
rekmarks Feb 3, 2026
f431659
fix(omnium-gatherum): Add console forwarding from kernel-worker and vats
rekmarks Feb 4, 2026
2f3d086
refactor(omnium): Rename bootstrap vat to controller vat
rekmarks Feb 4, 2026
4feef90
test(ocap-kernel): Tweak kernel facet tests
rekmarks Feb 4, 2026
23be8f7
fix(ocap-kernel,omnium-gatherum): Address PR review feedback
rekmarks Feb 4, 2026
63eaac4
test(nodejs): Add system vat e2e tests
rekmarks Feb 4, 2026
a5b98da
refactor(ocap-kernel): Rename system vats to system subclusters and a…
rekmarks Feb 4, 2026
0219613
refactor(ocap-kernel): Clean up orphaned system subclusters on kernel…
rekmarks Feb 4, 2026
b351ff9
refactor(nodejs): Convert system-vat test to TypeScript
rekmarks Feb 4, 2026
2152f04
fix(ocap-kernel): Throw on corrupted system subcluster state
rekmarks Feb 5, 2026
0f91b91
fix(omnium-gatherum): Restore controller vat from baggage on kernel r…
rekmarks Feb 5, 2026
ada3ebb
chore(cli): Add TODO for process.env.NODE_ENV injection
rekmarks Feb 5, 2026
4b3db2c
test(nodejs): Fix type errors in system-vat.ts
rekmarks Feb 5, 2026
4627815
fix(ocap-kernel,omnium-gatherum): Address PR review feedback
rekmarks Feb 5, 2026
15dcf8c
fix(nodejs): Increase connection rate limit in queue management test
rekmarks Feb 5, 2026
6672eb8
refactor: Consolidate KernelFacade into KernelFacet
rekmarks Feb 5, 2026
3b5d23e
refactor(ocap-kernel): Replace getVatRoot with getPresence on Kernel
rekmarks Feb 5, 2026
f660f31
refactor(ocap-kernel): Simplify makeKernelFacet to spread deps
rekmarks Feb 5, 2026
d37faa0
test(nodejs): Fix broken e2e test
rekmarks Feb 5, 2026
5b5b90f
refactor(ocap-kernel): Change Subcluster.vats from VatId[] to Record<…
rekmarks Feb 5, 2026
4f0810b
fix(kernel-browser-runtime): Wrap mock methods to survive harden() in…
rekmarks Feb 5, 2026
40f0b33
refactor(omnium): Add SystemSubclusterConfig type constraint
rekmarks Feb 5, 2026
7a8551e
fix(ocap-kernel): Update system subcluster mappings on reload
rekmarks Feb 6, 2026
59ba8f2
refactor(ocap-kernel): Move system subcluster lifecycle from Kernel t…
rekmarks Feb 6, 2026
ba08a8f
fix(extension): Update hardcoded ko IDs in e2e tests for kernelFacet …
rekmarks Feb 6, 2026
3af3e2e
fix(ocap-kernel): Update system subcluster mappings in reloadAllSubcl…
rekmarks Feb 6, 2026
9ce5379
refactor(omnium): Use proper Baggage type and simplify adapter
rekmarks Feb 6, 2026
a637417
refactor(omnium): Make logger non-optional in controllers
rekmarks Feb 6, 2026
8002ee5
refactor(ocap-kernel): Simplify makeKernelFacet to bind methods inter…
rekmarks Feb 6, 2026
a9ab2c2
refactor(nodejs): Use options bag for makeTestKernel
rekmarks Feb 6, 2026
7935dd7
chore: Extend vitest eslint rules to all test files
rekmarks Feb 6, 2026
6f7cff4
fix(omnium): Fix omnium.caplet global type declarations
rekmarks Feb 6, 2026
8691db8
test(nodejs): Clean up system sybcluster e2e tests
rekmarks Feb 6, 2026
87604ff
fix(ocap-kernel): Use hasProperty to check allowed globals
rekmarks Feb 6, 2026
cc6ceec
test(omnium): Add non-trivial e2e test
rekmarks Feb 6, 2026
9441379
fix(omnium): Reject capletFacetP on resuscitation failure
rekmarks Feb 6, 2026
31868d8
fix(ocap-kernel): Call provideFacet() unconditionally in Kernel#init
rekmarks Feb 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const config = createConfig([
},

{
files: ['**/*.test.ts', '**/*.test.tsx'],
files: ['**/test/**/*', '**/*.test.ts', '**/*.test.tsx'],
extends: [metamaskVitestConfig],
rules: {
// It's fine to do this in tests.
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/vite/vat-bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export async function bundleVat(sourcePath: string): Promise<VatBundle> {
const result = await build({
configFile: false,
logLevel: 'silent',
// TODO: Remove this define block and add a process shim to VatSupervisor
// workerEndowments instead. This injects into ALL bundles but is only needed
// for libraries like immer that check process.env.NODE_ENV.
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
Comment on lines +23 to +28
Copy link
Member Author

Choose a reason for hiding this comment

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

Going to address in a follow-up. Requires changes to how we bundle vats with Vite best not added to this PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

build: {
write: false,
lib: {
Expand Down
1 change: 1 addition & 0 deletions packages/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@metamask/kernel-ui": "workspace:^",
"@metamask/kernel-utils": "workspace:^",
"@metamask/logger": "workspace:^",
"@metamask/ocap-kernel": "workspace:^",
"@metamask/streams": "workspace:^",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/extension/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { KernelFacade } from '@metamask/kernel-browser-runtime';
import type { KernelFacet } from '@metamask/ocap-kernel';

// Type declarations for kernel dev console API.
declare global {
Expand All @@ -16,7 +16,7 @@ declare global {
var E: typeof import('@endo/eventual-send').E;

// eslint-disable-next-line no-var
var kernel: KernelFacade | Promise<KernelFacade>;
var kernel: KernelFacet | Promise<KernelFacet>;
}

export {};
18 changes: 9 additions & 9 deletions packages/extension/test/e2e/control-panel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,19 +191,19 @@ test.describe('Control Panel', () => {
const v3Values = [
'{"key":"e.nextPromiseId.v3","value":"2"}',
'{"key":"e.nextObjectId.v3","value":"1"}',
'{"key":"ko5.owner","value":"v3"}',
'{"key":"v3.c.ko5","value":"R o+0"}',
'{"key":"v3.c.o+0","value":"ko5"}',
'{"key":"ko6.owner","value":"v3"}',
'{"key":"v3.c.ko6","value":"R o+0"}',
'{"key":"v3.c.o+0","value":"ko6"}',
'{"key":"v3.c.kp4","value":"R p-1"}',
'{"key":"v3.c.p-1","value":"kp4"}',
'{"key":"ko5.refCount","value":"1,1"}',
'{"key":"ko6.refCount","value":"1,1"}',
'{"key":"kp4.refCount","value":"2"}',
];
const v1koValues = [
'{"key":"v1.c.ko4","value":"R o-1"}',
'{"key":"v1.c.o-1","value":"ko4"}',
'{"key":"v1.c.ko5","value":"R o-2"}',
'{"key":"v1.c.o-2","value":"ko5"}',
'{"key":"v1.c.ko5","value":"R o-1"}',
'{"key":"v1.c.o-1","value":"ko5"}',
'{"key":"v1.c.ko6","value":"R o-2"}',
'{"key":"v1.c.o-2","value":"ko6"}',
];
await expect(
popupPage.locator('[data-testid="message-output"]'),
Expand Down Expand Up @@ -263,7 +263,7 @@ test.describe('Control Panel', () => {
popupPage.locator('[data-testid="message-output"]'),
).not.toContainText(value);
}
// ko3 (vat root) reference still exists for v1
// v2/v3 vat root references still exist for v1
for (const value of v1koValues) {
await expect(
popupPage.locator('[data-testid="message-output"]'),
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/test/e2e/object-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ test.describe('Object Registry', () => {

test('should revoke an object', async () => {
const owner = 'v1';
const v1Root = 'ko3';
const v1Root = 'ko4';
const [target, method, params] = [v1Root, 'hello', '["Bob"]'];

// Before revoking, we should be able to send a message to the object
Expand Down
4 changes: 2 additions & 2 deletions packages/extension/test/e2e/remote-comms.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ test.describe('Remote Communications', () => {
await expect(targetSelect).toBeVisible();
const options = await targetSelect.locator('option').all();
expect(options.length).toBeGreaterThan(1);
await targetSelect.selectOption({ value: 'ko3' });
expect(await targetSelect.inputValue()).toBe('ko3');
await targetSelect.selectOption({ value: 'ko4' });
expect(await targetSelect.inputValue()).toBe('ko4');

// Set method to doRunRun (the remote communication method)
const methodInput = popupPage1.locator('[data-testid="message-method"]');
Expand Down
9 changes: 5 additions & 4 deletions packages/kernel-browser-runtime/src/background-captp.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { makeCapTP } from '@endo/captp';
import type { JsonRpcMessage, JsonRpcCall } from '@metamask/kernel-utils';
import type { KernelFacet } from '@metamask/ocap-kernel';
import type { JsonRpcNotification } from '@metamask/utils';

import type { CapTPMessage, KernelFacade } from './types.ts';
import type { CapTPMessage } from './types.ts';

export type { CapTPMessage };

Expand Down Expand Up @@ -79,12 +80,12 @@ export type BackgroundCapTP = {
dispatch: (message: CapTPMessage) => boolean;

/**
* Get the remote kernel facade.
* Get the remote kernel facet.
* This is how the background calls kernel methods using E().
*
* @returns A promise for the kernel facade remote presence.
* @returns A promise for the kernel facet remote presence.
*/
getKernel: () => Promise<KernelFacade>;
getKernel: () => Promise<KernelFacet>;

/**
* Abort the CapTP connection.
Expand Down
1 change: 0 additions & 1 deletion packages/kernel-browser-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export * from './makeIframeVatWorker.ts';
export * from './PlatformServicesClient.ts';
export * from './PlatformServicesServer.ts';
export * from './utils/index.ts';
export type { KernelFacade } from './types.ts';
export {
makeBackgroundCapTP,
isCapTPNotification,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { E } from '@endo/eventual-send';
import type { ClusterConfig, Kernel } from '@metamask/ocap-kernel';
import { makeKernelFacet, kslot } from '@metamask/ocap-kernel';
import { describe, it, expect, vi, beforeEach } from 'vitest';

import { makeKernelCapTP } from './kernel-captp.ts';
Expand All @@ -10,7 +11,7 @@ import type { CapTPMessage } from '../../background-captp.ts';
* Integration tests for CapTP communication between background and kernel endpoints.
*
* These tests validate that the two CapTP endpoints can communicate correctly
* and that E() works properly with the kernel facade remote presence.
* and that E() works properly with the kernel facet remote presence.
*/
describe('CapTP Integration', () => {
let mockKernel: Kernel;
Expand All @@ -20,31 +21,59 @@ describe('CapTP Integration', () => {
beforeEach(() => {
// Create mock kernel with method implementations
mockKernel = {
getPresence: vi
.fn()
.mockImplementation(async (kref: string, iface: string) =>
kslot(kref, iface),
),
getStatus: vi.fn().mockResolvedValue({
vats: [{ id: 'v1', name: 'test-vat' }],
subclusters: ['sc1'],
remoteComms: false,
}),
getSubcluster: vi.fn().mockReturnValue(undefined),
getSubclusters: vi.fn().mockReturnValue([]),
getSystemSubclusterRoot: vi.fn().mockReturnValue('ko99'),
launchSubcluster: vi.fn().mockResolvedValue({
subclusterId: 'sc1',
bootstrapRootKref: 'ko1',
rootKref: 'ko1',
bootstrapResult: {
body: '#{"result":"ok"}',
slots: [],
},
}),
terminateSubcluster: vi.fn().mockResolvedValue(undefined),
pingVat: vi.fn().mockResolvedValue('pong'),
queueMessage: vi.fn().mockResolvedValue({
body: '#{"result":"message-sent"}',
slots: [],
}),
getStatus: vi.fn().mockResolvedValue({
vats: [{ id: 'v1', name: 'test-vat' }],
subclusters: ['sc1'],
remoteComms: false,
}),
pingVat: vi.fn().mockResolvedValue('pong'),
reloadSubcluster: vi.fn().mockResolvedValue({ id: 'sc1', vats: [] }),
reset: vi.fn().mockResolvedValue(undefined),
terminateSubcluster: vi.fn().mockResolvedValue(undefined),
provideFacet: vi.fn(),
} as unknown as Kernel;

// Wire up provideFacet to return a real facet backed by the mock kernel.
// We wrap each mock method with a delegate so that harden() inside
// makeKernelFacet (via makeDefaultExo) freezes the wrapper functions
// instead of the original vi.fn() instances, keeping call tracking intact.
vi.mocked(mockKernel.provideFacet).mockReturnValue(
makeKernelFacet(
Object.fromEntries(
Object.entries(mockKernel)
.filter(
(entry): entry is [string, (...args: never[]) => unknown] =>
typeof entry[1] === 'function',
)
.map(([key, fn]) => [key, (...args: never[]) => fn(...args)]),
) as unknown as Kernel,
),
);

// Wire up CapTP endpoints to dispatch messages synchronously to each other
// This simulates direct message passing for testing

// Kernel-side: exposes facade as bootstrap
// Kernel-side: exposes facet as bootstrap
kernelCapTP = makeKernelCapTP({
kernel: mockKernel,
send: (message: CapTPMessage) => {
Expand All @@ -64,7 +93,7 @@ describe('CapTP Integration', () => {

describe('bootstrap', () => {
it('background can get kernel remote presence via getKernel', async () => {
// Request the kernel facade - with synchronous dispatch, this resolves immediately
// Request the kernel facet - with synchronous dispatch, this resolves immediately
const kernel = await backgroundCapTP.getKernel();
expect(kernel).toBeDefined();
});
Expand Down Expand Up @@ -115,10 +144,14 @@ describe('CapTP Integration', () => {
// Call launchSubcluster via E()
const result = await E(kernel).launchSubcluster(config);

// The kernel facade now returns LaunchResult instead of CapData
// The kernel facet delegates to the kernel's launchSubcluster
expect(result).toStrictEqual({
subclusterId: 'sc1',
rootKref: 'ko1',
bootstrapResult: {
body: '#{"result":"ok"}',
slots: [],
},
});

expect(mockKernel.launchSubcluster).toHaveBeenCalledWith(config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@ export {
type KernelCapTP,
type KernelCapTPOptions,
} from './kernel-captp.ts';

export { makeKernelFacade, type KernelFacade } from './kernel-facade.ts';
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import type { Kernel } from '@metamask/ocap-kernel';
import { makeKernelFacet, kslot } from '@metamask/ocap-kernel';
import { describe, it, expect, vi, beforeEach } from 'vitest';

import { makeKernelCapTP } from './kernel-captp.ts';
import type { CapTPMessage } from '../../types.ts';

describe('makeKernelCapTP', () => {
const mockKernel: Kernel = {} as unknown as Kernel;
const mockKernel = {
getPresence: vi
.fn()
.mockImplementation(async (kref: string, iface: string) =>
kslot(kref, iface),
),
getStatus: vi.fn(),
getSubcluster: vi.fn(),
getSubclusters: vi.fn(),
getSystemSubclusterRoot: vi.fn(),
launchSubcluster: vi.fn(),
pingVat: vi.fn(),
queueMessage: vi.fn(),
reloadSubcluster: vi.fn(),
reset: vi.fn(),
terminateSubcluster: vi.fn(),
provideFacet: vi.fn(),
} as unknown as Kernel;

vi.mocked(mockKernel.provideFacet).mockReturnValue(
makeKernelFacet(mockKernel),
);
let sendMock: (message: CapTPMessage) => void;

beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { makeCapTP } from '@endo/captp';
import type { Kernel } from '@metamask/ocap-kernel';

import { makeKernelFacade } from './kernel-facade.ts';
import type { CapTPMessage } from '../../types.ts';

/**
Expand Down Expand Up @@ -44,7 +43,7 @@ export type KernelCapTP = {
/**
* Create a CapTP endpoint for the kernel.
*
* This sets up a CapTP connection that exposes the kernel facade as the
* This sets up a CapTP connection that exposes the kernel facet as the
* bootstrap object. The background can then use `E(kernel).method()` to
* call kernel methods.
*
Expand All @@ -54,11 +53,8 @@ export type KernelCapTP = {
export function makeKernelCapTP(options: KernelCapTPOptions): KernelCapTP {
const { kernel, send } = options;

// Create the kernel facade that will be exposed to the background
const kernelFacade = makeKernelFacade(kernel);

// Create the CapTP endpoint
const { dispatch, abort } = makeCapTP('kernel', send, kernelFacade);
// Create the CapTP endpoint with the kernel facet as the bootstrap object
const { dispatch, abort } = makeCapTP('kernel', send, kernel.provideFacet());

return harden({
dispatch,
Expand Down
Loading
Loading