Skip to content

feat(netlayer): extract @metamask/netlayer-libp2p + flip runtime injection (phase 4)#973

Draft
rekmarks wants to merge 8 commits into
rekm/netlayer-3from
rekm/netlayer-4
Draft

feat(netlayer): extract @metamask/netlayer-libp2p + flip runtime injection (phase 4)#973
rekmarks wants to merge 8 commits into
rekm/netlayer-3from
rekm/netlayer-4

Conversation

@rekmarks

@rekmarks rekmarks commented Jul 11, 2026

Copy link
Copy Markdown
Member

Phase 4 of the pluggable-netlayer effort (issue #968), stacked on #972 (rekm/netlayer-3). Two coherent commits: 4a extracts the libp2p implementation into a new package; 4b flips control so runtimes inject a netlayer registry.

4a — Extract @metamask/netlayer-libp2p

  • New published package with . (browser libp2pNetlayerFactory), ./nodejs (QUIC/TCP nodejsLibp2pNetlayerFactory, moved from kernel-node-runtime), and ./relay (startRelay, moved from kernel-utils/libp2p).
  • Moved connection-factory, lp-framing, utils/multiaddr, utils/network, provider-only constants, and extracted the read-error mapper into error-mapper.ts.
  • Folded the libp2p MuxerClosedError/Dial/Transport/NO_RESERVATION name-sniffing out of @metamask/kernel-errors into the provider: connection-factory.dial now maps raw libp2p errors onto neutral kernel-errors so the transport-neutral engine classifies reconnection identically (behavior-preserving; PERMANENT_FAILURE_ERROR_CODES are Node-code-only). isRetryableNetworkError/getNetworkErrorCode reduced to the neutral subset.
  • kernel-cli relay command + e2e consumers repointed; ocap-kernel and kernel-utils lost their direct libp2p deps.

4b — Flip runtime injection

  • PlatformServices.initializeRemoteComms is now an options bag ({ keySeed, specifier, hooks, incarnationId? }); NodejsPlatformServices and PlatformServicesServer/Client take a NetlayerRegistry. make-kernel defaults to { libp2p: nodejsLibp2pNetlayerFactory } and accepts a netlayers override.
  • RemoteCommsOptions shrinks to kernel-owned fields + specifier; remote-comms.ts injects the persisted hint pool as config.knownRelays and keeps mnemonic out of the Json config. RPC superstructs use JsonStruct for config; kernel-cli daemon, extension/omnium offscreen.ts, and comms-query-string.ts pass a specifier.
  • packages/ocap-kernel/package.json now has zero libp2p/@libp2p/@chainsafe/@multiformats deps (plain multiformats stays — neutral base58btc). initTransport export removed.

For reviewer attention

  • @libp2p/webrtc endoify relocation (phase-4.md §3.7) is DEFERRED. kernel-shims keeps the pre-lockdown import '@libp2p/webrtc' in endoify-node.js. Rationale: every @metamask/kernel-node-runtime importer transitively loads @libp2p/webrtc at module-eval (via make-kernel → the nodejs factory → connection-factory), so relocating the pre-lockdown import means repointing ~7 packages' entrypoints/setup files with a silent-lockdown-failure risk if any is missed. It is not required for the zero-libp2p gate (that gate is ocap-kernel only). Flagging for a human decision on whether to pursue it separately.
  • Phase 5 (@metamask/netlayer-websocket) is DEFERRED per a user directive: the effort skips from Phase 4 straight to Phase 6 (cleanup + docs). This PR's doc updates reflect that — master.md marks Phase 5 deferred and sequences Phase 6 after Phase 4, phase-5.md carries a prominent deferral banner, and phase-6.md is reconciled against landed Phases 1–4 with every WebSocket-netlayer reference reworded as future work.
  • The browser PlatformServicesServer does not capture getListenAddresses (the client returns [] for Node-only direct transport; capturing it would be dead code that fails the unused-private-member lint).

Verification

  • yarn lint:fix, yarn build, yarn test:dev:quiet all green (58 turbo tasks).
  • Coverage: every touched package meets or exceeds its pre-phase baseline; the new package lands at 96.75% stmts / 94.11% branch. Extraction dips (ocap-kernel funcs/branch, kernel-errors branch) closed with behavior-asserting tests.
  • Extension bundle: offscreen.js unchanged (~1,320 kB / 435 kB gzip) — libp2p was already transitively bundled.

🤖 Generated with Claude Code

rekmarks and others added 6 commits July 10, 2026 17:05
Move the libp2p netlayer implementation out of @MetaMask/ocap-kernel into a
new @metamask/netlayer-libp2p package, with no runtime behavior change. The
package exports the browser-default `libp2pNetlayerFactory` (`.`), a Node.js
factory with direct QUIC/TCP transports (`./nodejs`), and the circuit-relay
server (`./relay`).

- Move connection-factory, lp-framing test, multiaddr/network utils, and the
  provider-only constants into the new package.
- Extract the module-private read-error mapper into `error-mapper.ts`, and fold
  the libp2p `MuxerClosedError`/`Dial`/`Transport`/`NO_RESERVATION`
  name-sniffing out of `@metamask/kernel-errors` into the provider: the libp2p
  `dial` now maps raw errors onto neutral kernel-errors so the transport-neutral
  engine classifies reconnection identically. `isRetryableNetworkError` /
  `getNetworkErrorCode` are reduced to the neutral subset.
- Move the relay server from `@metamask/kernel-utils/libp2p` to
  `@metamask/netlayer-libp2p/relay`; repoint kernel-cli and e2e consumers.
- ocap-kernel keeps a temporary `initTransport` compatibility shim that
  delegates to the browser factory (never reaching the `./nodejs` subpath, so
  the extension bundle is unaffected); `directTransports` keeps flowing from
  `NodejsPlatformServices`. Remove all direct libp2p deps from ocap-kernel and
  kernel-utils.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Invert control so runtimes inject a `NetlayerRegistry` and callers select an
implementation with a `NetlayerSpecifier` (`{ netlayer, config: Json }`) instead
of the kernel hard-wiring libp2p.

- `PlatformServices.initializeRemoteComms` becomes an options bag
  (`{ keySeed, specifier, hooks, incarnationId? }`); `NodejsPlatformServices`
  and `PlatformServicesServer`/`Client` are constructed with a
  `NetlayerRegistry`. `make-kernel` defaults to `{ libp2p:
  nodejsLibp2pNetlayerFactory }` and accepts a `netlayers` override.
- `RemoteCommsOptions` shrinks to the kernel-owned fields plus the specifier;
  `remote-comms.ts` injects the persisted hint pool as `config.knownRelays` and
  keeps `mnemonic` out of the `Json` config. `DirectTransport` /
  `ConnectionFactoryOptions` are removed.
- RPC superstructs (`rpc/platform-services/`, `rpc/kernel-control/`) carry the
  specifier with `JsonStruct` config; kernel-cli daemon, extension/omnium
  `offscreen.ts`, and `comms-query-string.ts` pass a specifier.
- Remove the `initTransport` re-export and ALL libp2p deps from ocap-kernel;
  `packages/ocap-kernel/package.json` now has zero libp2p-family deps.

Deviations from phase-4.md: the endoify-node `@libp2p/webrtc` relocation
(§3.7) is deferred (kept in kernel-shims) — every kernel-node-runtime importer
transitively loads webrtc, so relocating risks silent pre-lockdown failures
across ~7 packages; it is not required for the zero-libp2p gate (ocap-kernel).
The browser `PlatformServicesServer` does not capture `getListenAddresses`
(the client returns `[]` for Node-only direct transport; capturing would be
dead code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add Unreleased changelog entries (linking #973) for @metamask/netlayer-libp2p
(new), ocap-kernel, kernel-errors, kernel-utils, kernel-node-runtime,
kernel-browser-runtime, and kernel-cli. Mark Phase 4 done in master.md with a
Landed-decisions block, and reconcile phase-5.md against the landed contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Node test helpers and evm-wallet-experiment runners construct
`NodejsPlatformServices` directly (not via `make-kernel`), so they must pass the
now-required `netlayers` registry. Without it, `initializeRemoteComms` threw
`Cannot read properties of undefined (reading 'libp2p')`, failing the
kernel-node-runtime remote-comms/relay e2e suites and the evm-wallet peer-wallet
integration tests.

- `kernel-node-runtime/test/helpers/kernel.ts` and `kernel-test` ctors pass
  `{ libp2p: nodejsLibp2pNetlayerFactory }`.
- All `evm-wallet-experiment` runners/scripts/tests do the same; add
  `@metamask/netlayer-libp2p` to that package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pre-injection e2e and integration tests passed libp2p-specific
options (relays, directListenAddresses, backoff timings) at the top
level of initRemoteComms. After the netlayer-injection flip these fields
belong in specifier.config, so they were silently dropped and
connections failed. Introduce a libp2pComms test helper that maps the
flat shape into the injected RemoteCommsOptions and route all e2e call
sites through it (or an inline specifier).

Also add explicit tsconfig path mappings for the netlayer-libp2p
./nodejs and ./relay subpaths so vitest resolves them to source rather
than dist, keeping a single lockdown-time @libp2p/webrtc instance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 72.31%
⬆️ +0.95%
9063 / 12533
🔵 Statements 72.14%
⬆️ +0.95%
9214 / 12772
🔵 Functions 73.42%
⬆️ +0.85%
2194 / 2988
🔵 Branches 65.76%
⬆️ +0.90%
3659 / 5564
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/extension/src/offscreen.ts 0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
20-112
packages/kernel-browser-runtime/src/PlatformServicesClient.ts 94.73%
⬆️ +2.53%
87.09%
🟰 ±0%
83.33%
⬆️ +7.33%
96%
⬆️ +2.58%
112, 134, 420, 442
packages/kernel-browser-runtime/src/PlatformServicesServer.ts 95.04%
⬆️ +4.32%
90.32%
⬆️ +7.57%
80%
⬆️ +4.00%
96%
⬆️ +4.34%
149, 174, 196, 224, 461
packages/kernel-browser-runtime/src/utils/comms-query-string.ts 97.56%
⬇️ -2.44%
91.66%
⬇️ -8.34%
100%
🟰 ±0%
97.56%
⬇️ -2.44%
99-101
packages/kernel-cli/src/commands/daemon.ts 40%
🟰 ±0%
39.13%
🟰 ±0%
42.85%
🟰 ±0%
41.02%
🟰 ±0%
54-154, 169, 248-313, 393
packages/kernel-cli/src/commands/relay.ts 90.12%
🟰 ±0%
98.03%
🟰 ±0%
63.63%
🟰 ±0%
93.5%
🟰 ±0%
139, 145-151, 202, 209
packages/kernel-errors/src/utils/getNetworkErrorCode.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-errors/src/utils/isRetryableNetworkError.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-node-runtime/src/kernel/PlatformServices.ts 94.68%
⬆️ +1.76%
92%
⬆️ +2.26%
88.88%
⬆️ +4.67%
94.68%
⬆️ +1.76%
150-153, 191, 333-336
packages/kernel-node-runtime/src/kernel/make-kernel.ts 100%
🟰 ±0%
88.88%
⬆️ +3.17%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-test/src/utils.ts 86.95%
🟰 ±0%
70.58%
🟰 ±0%
94.44%
🟰 ±0%
86.66%
🟰 ±0%
44, 118, 123, 164-179
packages/netlayer-libp2p/src/config.ts 100% 100% 100% 100%
packages/netlayer-libp2p/src/error-mapper.ts 100% 100% 100% 100%
packages/netlayer-libp2p/src/index.ts 100% 100% 100% 100%
packages/netlayer-libp2p/src/make-libp2p-netlayer.ts 100% 100% 100% 100%
packages/netlayer-libp2p/src/types.ts 100% 100% 100% 100%
packages/netlayer-libp2p/src/nodejs/direct-transports.ts 100% 100% 100% 100%
packages/netlayer-libp2p/src/nodejs/index.ts 100% 100% 100% 100%
packages/ocap-kernel/src/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/ocap-kernel/src/types.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/ocap-kernel/src/remotes/types.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/ocap-kernel/src/remotes/kernel/remote-comms.ts 99.35%
⬆️ +0.08%
97.5%
⬆️ +0.54%
100%
🟰 ±0%
99.34%
⬆️ +0.09%
361
packages/ocap-kernel/src/rpc/kernel-control/init-remote-comms.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/ocap-kernel/src/rpc/platform-services/initializeRemoteComms.ts 100%
⬆️ +21.74%
100%
⬆️ +27.78%
100%
🟰 ±0%
100%
⬆️ +21.74%
packages/omnium-gatherum/src/offscreen.ts 0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
21-132
Generated in workflow #4517 for commit 961d968 by the Vitest Coverage Report Action

rekmarks and others added 2 commits July 10, 2026 19:32
Per a user directive, the WebSocket netlayer (phase 5) is deferred and
the effort skips straight to phase 6 (cleanup + docs). Mark phase 5
deferred in master.md and adjust the sequence/estimate so phase 6
follows phase 4. Add a prominent deferral banner to phase-5.md. Revise
phase-6.md against landed phases 1-4 and the phase 5 deferral: reword
every reference to the WebSocket netlayer (package, hub/handshake,
kernel-test parameterization, changelog row, README) as future work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With phase 5 deferred, reword the endoify/@libp2p/webrtc relocation note
in master.md from a "phase-5/6" to a phase-6/future follow-up, and add a
dedicated "follow-ups carried from phase 4" section to phase-6.md
capturing the four items that now land in phase 6's lap: the review-gated
webrtc pre-lockdown relocation, the browser getListenAddresses gap, the
libp2pComms test-helper retirement, and the load-bearing tsconfig
subpath-mapping rule for netlayer-libp2p.

Co-Authored-By: Claude Fable 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.

1 participant