You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Closes#936.
Integrates the Snaps `@metamask/snaps-execution-environments/endowments`
network factory into vat globals. Replaces the platform-level `fetch`
capability with a per-vat endowment that ships
`fetch`/`Request`/`Headers`/`Response`, wires teardown for in-flight
requests and open body streams, and enforces a per-vat host allowlist
(`VatConfig.network.allowedHosts`) applied at `#initVat` via
`makeHostCaveat`. `fetch` without an allowlist fails init — no implicit
allow-all. `file://` URLs are rejected by the caveat with a hint to use
the `fs` platform capability.
## Summary
- Add `fetch`, `Request`, `Headers`, `Response` to the default vat
endowments via the Snaps factory; teardown aggregates into
`VatSupervisor.terminate()`.
- New `VatConfig.network: { allowedHosts: string[] }`; host matching is
`URL.hostname`-only (ports and schemes ignored).
- Wire the Snaps factory's `notify` callback to the vat logger at
`debug`; transport failures surface via `console.error`.
- Remove the entire `packages/kernel-platforms/src/capabilities/fetch/`
directory (BREAKING for direct consumers of `fetchConfigStruct`,
`FetchCapability`, `makeHostCaveat`, `makeCaveatedFetch`).
- Migrate consumers: `kernel-node-runtime` drops
`platformOptions.fetch.fromFetch`; `nodejs-test-workers/mock-fetch`
stubs `globalThis.fetch` directly; `evm-wallet-experiment` cluster
config and docker e2e helper move to `globals: ['fetch', ...]` +
`network.allowedHosts`.
- Update `docs/kernel-guide.md` endowment table, `docs/usage.md`, and
`packages/evm-wallet-experiment/docs/setup-guide.md`.
## Test plan
- Unit: `network-caveat.test.ts` covers host/hostname matching,
port-insensitivity, `file://` rejection (string + Request input),
malformed URL propagation, and a positive+negative end-to-end chain
through `makeCaveatedFetch`.
- `endowments.test.ts` verifies the new globals list, the `notify`
wiring, logger-transport error containment, and that teardown aborts an
in-flight `fetch` (stubs `globalThis.fetch` with an abort-respecting
promise, asserts the abort signal propagates and teardown returns
cleanly).
- `VatSupervisor.test.ts` exercises both the fetch-without-allowlist
throw path and the positive-path where `network.allowedHosts` is
supplied (asserts absence of the guard error in dispatch).
- `types.test.ts` validates the new `network` field, rejecting non-array
and non-string entries.
- Integration: `kernel-test/src/endowments.test.ts` exercises
allowed/disallowed hosts end-to-end through a real `VatSupervisor` and
asserts `Request`/`Headers`/`Response` constructors are available in the
vat compartment.
- Lint + changelog validate clean across all three touched packages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Moderate risk due to **breaking configuration/API changes** (removing
platform-level `fetch`) and new network enforcement in `VatSupervisor`
that can cause vats to fail `initVat` if misconfigured; changes touch
security-sensitive outbound network gating but are covered by new
unit/integration tests.
>
> **Overview**
> Adds a Snaps-provided **network endowment** (`fetch` +
`Request`/`Headers`/`Response`) to vat globals and enforces a new
per-vat allowlist via `VatConfig.network.allowedHosts`; requesting
`fetch` without this allowlist now fails `initVat`, and the caveat
rejects `file://` targets.
>
> Removes the `fetch` **platform capability** from `kernel-platforms`
(and updates downstream configs/tests) and updates supervisors/endowment
factories to accept a `{ logger }` options bag, wiring Snaps network
`notify` events into debug logging and ensuring teardown cancels
in-flight requests.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
21ba678. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|`setTimeout`| Timer (attenuated) | Isolated per vat. Cancelled automatically on vat termination. |
161
-
|`clearTimeout`| Timer (attenuated) | Only clears timers created by the same vat. |
162
-
|`setInterval`| Timer (attenuated) | Isolated per vat. Cancelled automatically on vat termination. |
163
-
|`clearInterval`| Timer (attenuated) | Only clears intervals created by the same vat. |
164
-
|`Date`| Attenuated | Each `Date.now()` read adds up to 1 ms of random jitter, clamped monotonic non-decreasing; precise sub-millisecond timing cannot leak through. |
165
-
|`Math`| Attenuated |`Math.random()` is sourced from `crypto.getRandomValues`. **Not a CSPRNG** per the upstream NOTE — defends against stock-RNG timing side channels only. |
166
-
|`crypto`| Web Crypto | Hardened Web Crypto API. |
167
-
|`SubtleCrypto`| Web Crypto | Hardened Web Crypto API. |
|`setTimeout`| Timer (attenuated) | Isolated per vat. Cancelled automatically on vat termination. |
161
+
|`clearTimeout`| Timer (attenuated) | Only clears timers created by the same vat. |
162
+
|`setInterval`| Timer (attenuated) | Isolated per vat. Cancelled automatically on vat termination. |
163
+
|`clearInterval`| Timer (attenuated) | Only clears intervals created by the same vat. |
164
+
|`Date`| Attenuated | Each `Date.now()` read adds up to 1 ms of random jitter, clamped monotonic non-decreasing; precise sub-millisecond timing cannot leak through. |
165
+
|`Math`| Attenuated |`Math.random()` is sourced from `crypto.getRandomValues`. **Not a CSPRNG** per the upstream NOTE — defends against stock-RNG timing side channels only. |
166
+
|`crypto`| Web Crypto | Hardened Web Crypto API. |
167
+
|`SubtleCrypto`| Web Crypto | Hardened Web Crypto API. |
168
+
|`fetch`| Network (attenuated) | Wrapped by the Snaps network factory; teardown aborts in-flight requests and cancels open body streams on vat termination. **Requires `network.allowedHosts`** — see [Network endowment](#network-endowment). |
169
+
|`Request`| Network | Hardened constructor surfaced alongside `fetch` so vat code can build requests before calling it. |
170
+
|`Headers`| Network | Hardened constructor. |
171
+
|`Response`| Network | Hardened constructor; overrides `[Symbol.hasInstance]` so wrapped fetch results still pass `instanceof Response`. |
172
+
|`TextEncoder`| Text codec | Plain hardened. |
173
+
|`TextDecoder`| Text codec | Plain hardened. |
174
+
|`URL`| URL | Plain hardened. |
175
+
|`URLSearchParams`| URL | Plain hardened. |
176
+
|`atob`| Base64 | Plain hardened. |
177
+
|`btoa`| Base64 | Plain hardened. |
178
+
|`AbortController`| Abort | Plain hardened. |
179
+
|`AbortSignal`| Abort | Plain hardened. |
176
180
177
181
"Plain hardened" means the value is the host's implementation wrapped with `harden()` — it behaves identically to the browser/Node version. "Attenuated" means the value is a deliberate reimplementation with different semantics; the Notes column flags the relevant differences. The canonical list lives in [`endowments.ts`](../packages/ocap-kernel/src/vats/endowments.ts).
178
182
183
+
### Network endowment
184
+
185
+
`fetch`, `Request`, `Headers`, and `Response` are only available when the vat also declares a per-vat host allowlist in `VatConfig.network.allowedHosts`:
Requesting `'fetch'` without an `allowedHosts` entry (or with an absent `network` block) fails `initVat` with `Vat "<id>" requested "fetch" but no network.allowedHosts was specified`. There is no implicit allow-all; an empty `allowedHosts: []` is legal but rejects every outbound host. Host matching is a case-sensitive exact comparison against `URL.hostname` — ports and schemes are not considered, so `allowedHosts: ['api.example.com']` accepts both `http://api.example.com` and `https://api.example.com:8443`. `file://` URLs are **rejected** by fetch — use the `fs` platform capability for filesystem access.
201
+
202
+
Lifecycle notes:
203
+
204
+
- The network factory reads `globalThis.fetch` at call time — host applications that need to stub it (e.g., tests) should override the global before constructing the `VatSupervisor`.
205
+
- Teardown cancels in-flight requests and open body streams. It runs as part of `VatSupervisor.terminate()` alongside timer teardown.
206
+
-`fetch` returns a `ResponseWrapper` rather than the raw `Response`; the endowed `Response` constructor is patched so `instanceof Response` still returns `true` for wrapper instances.
207
+
179
208
### Restricting or replacing the allowed set
180
209
181
210
Two levers, applied at different layers:
@@ -536,8 +565,9 @@ type VatConfig = {
536
565
bundleName?:string; // Name of a pre-registered bundle
537
566
creationOptions?:Record<string, Json>; // Options for vat creation
538
567
parameters?:Record<string, Json>; // Static parameters passed to buildRootObject
Copy file name to clipboardExpand all lines: docs/usage.md
+15Lines changed: 15 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -207,6 +207,21 @@ A vat can also request host/Web API globals (timers, `Date`, `crypto`, `URL`,
207
207
}
208
208
```
209
209
210
+
Network access is a special case: requesting `fetch` (and optionally `Request`/`Headers`/`Response`) also requires a per-vat host allowlist under `network.allowedHosts`. Without it, `initVat` rejects the vat.
See [Vat Endowments](./kernel-guide.md#vat-endowments) in the kernel guide for the full list and for how to narrow the set with `Kernel.make({ allowedGlobalNames })`.
Copy file name to clipboardExpand all lines: packages/kernel-platforms/CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Removed
11
+
12
+
-**BREAKING:** Remove the `fetch` platform capability and its exports (`fetchConfigStruct`, `FetchCapability`, `FetchConfig`, `makeHostCaveat`, `makeCaveatedFetch`) ([#942](https://github.com/MetaMask/ocap-kernel/pull/942))
13
+
-`fetch` is now a vat endowment in `@metamask/ocap-kernel`; see its changelog for the migration
0 commit comments