Skip to main content
Every meow project runs in one of two modes. The mode decides which global surface your code sees and whether the run is deterministic by default. You set it once in meow.config.json:
meow.config.json
{ "mode": "strict-web" }

The two modes at a glance

strict-web

A portable, web-standard sandbox. Only WinterTC-style web globals are present; Node host APIs are withdrawn. Deterministic by default. Great for libraries, edge/serverless functions, and anything you want reproducible.

node-compat

The full Node.js surface: node:* built-ins, CommonJS, process, fs, child_process, N-API. Full host access by default. Use it for Node apps and frameworks like Next.js, Vite, Astro.
Two different defaults.
  • meow init writes { "mode": "strict-web" }. A freshly scaffolded project is deterministic.
  • A project with no meow.config.json at all falls back to node-compat, so dropping meow into an existing Node repo behaves like Node from the first run.

What each mode exposes

Capabilitystrict-webnode-compat
fetch, Request, Response, Headers, FormData
URL, URLPattern, URLSearchParams
crypto.subtle, TextEncoder/TextDecoder, Blob
AbortController/AbortSignal, setTimeout, console
meow:http, meow:ui built-in modules
node:* built-ins (fs, path, crypto, os, …)❌ withdrawn
process global, process.env, process.argv❌ removed
CommonJS require()
npm packages that need Node built-ins
N-API native addons, child_process
Clock / randomness / envdeterministicreal host
In strict-web, touching a withdrawn Node API is a clear, fix-pointing error rather than a silent undefined:
import fs from "node:fs";       // import resolves...
fs.readFileSync("x");           // ...but throws ERR_STRICT_WEB_WITHDRAWN at use
Both modes share the same web layer — fetch and friends are always present. The mode only changes the Node host surface and the determinism defaults. The full strict-web global list is on the Web APIs page.

Determinism differs by mode

This is the part that surprises people, so it’s worth stating directly.
By default the run is hermetic:
  • Date.now() / new Date() return a fixed virtual epoch, advancing only as timers fire.
  • Math.random() and crypto.getRandomValues() draw from a seeded ChaCha20 stream.
  • The timezone is pinned to UTC and the locale fixed, so Date/Intl render identically everywhere.
  • Environment variables are invisible (process.env is empty; there is no process global at all).
Two machines run the same code and get the same bytes. Opt back into the real host per-capability with --allow-clock, --allow-random, --allow-env, or --trust. See Determinism and Permissions.
meow test always runs in a deterministic strict-web isolate, regardless of your project mode. Tests are reproducible by construction. If a test needs the real clock or host env, that’s a deliberate exception you grant — not the default.

Choosing a mode

  • You’re writing a library, an edge/serverless handler, or a Cloudflare-Workers-style function.
  • You want reproducible builds and tests with no hidden host dependencies.
  • Your code targets web standards (fetch, Request/Response, crypto.subtle) and doesn’t need node:fs or process.
  • You’re running Next.js, Vite, Astro, Playwright, Puppeteer, or any Node framework.
  • Your dependencies reach for node:fs, child_process, process.env, or native addons.
  • You’re migrating an existing Node app and want it to behave like Node from day one.
meow init                      # strict-web (default)
meow init --mode node-compat   # full Node surface
You can change the mode at any time by editing meow.config.json and re-running meow sync.

Next: how determinism actually works

The clock, RNG, and env seam that powers strict-web.