A browser-like runtime in Rust, built without Chromium.
ROM composes an embedded JavaScript engine, browser-facing host objects, and a compatibility-driven runtime
for deterministic web automation, surface emulation, and browser API research.
Most browser automation stacks start by shipping a full browser. ROM starts from the opposite direction:
- keep the runtime small and programmable
- emulate the browser surface incrementally
- validate compatibility against browser-facing probes and real harnesses
- stay transparent enough that the runtime can be inspected, extended, and reasoned about
The result is a native Rust workspace that aims to feel browser-like at the API layer without inheriting the operational weight of Chromium.
- A Rust workspace with a lightweight embedded JavaScript engine.
- A growing browser API compatibility layer.
- A runtime designed around deterministic probes, snapshots, and acceptance harnesses.
- A Rust core with JS and Python bindings that can use either native extensions or the CLI bridge.
- Not a full browser engine.
- Not a layout engine or Chromium replacement.
- Not production-complete yet.
- Not claiming full Web Platform coverage.
| Area | Current State |
|---|---|
| Runtime core | Embedded JavaScript runtime lifecycle, script execution, error handling |
| Web platform | fetch, streams, blobs, files, URLs, parser, workers, messaging, cookies, SSE, WebSocket |
| Crypto | digest, HMAC, AES-CTR, AES-CBC, AES-GCM, AES-KW, PBKDF2, HKDF |
| Validation | Browser-like parameter validation, JWK validation, key usage validation, import/export edge handling |
| Compatibility | surface_snapshot(), fingerprint_probe(), vendored FingerprintJS harness, browser reference runner |
| Media and device surface | permissions, media devices, plugins, mime types, viewport, orientation, media queries |
fetchHeaders,Request,ResponseReadableStream-basedRequest.bodyandResponse.body- redirect modes:
follow,error,manual - CORS response gating and preflight validation
AbortController,AbortSignalBlob,File,FormDataURL,URLSearchParams,URLPatternDOMParserblob:object URLs- cookies via
document.cookie,Cookie, andSet-Cookie
MessageEvent,MessagePort,MessageChannelBroadcastChannelWorkerwithBlobURL scripts,postMessage(), andimportScripts()EventSourcewithretry, reconnect, custom events,lastEventId, andclose()WebSocketwithws:andwss:, text and binary frames,Blobpayloads, and close events
crypto.getRandomValues()crypto.randomUUID()crypto.subtle.digest()forSHA-1,SHA-256,SHA-384,SHA-512- HMAC
generateKey(),importKey(),exportKey(),sign(),verify() AES-CTR,AES-CBC,AES-GCMgenerateKey(),importKey(),exportKey(),encrypt(),decrypt()AES-GCM128/192/256-bit keys and tag lengths96..128AES-KWwrapping flowsPBKDF2andHKDFforimportKey(),deriveBits(),deriveKey()- browser-like secret-key validation for length, usages, params, JWK content, import/export, wrap/unwrap payloads, and derive semantics
structuredClone()FileReadernavigator.permissions.query()navigator.mediaDevicesnavigator.userAgentDatanavigator.plugins,navigator.mimeTypes,navigator.pdfViewerEnabled- viewport globals,
visualViewport,screen.orientation,matchMedia() MutationObserver,ResizeObserver,IntersectionObserver- DOM event propagation with capture, bubble,
once, propagation stopping, andcomposedPath()
The workspace is intentionally split into three layers:
crates/rom-coreRaw embedded JavaScript engine wrapper.crates/rom-webapiBrowser API bootstrap and compatibility shims.crates/rom-runtimeHigh-level environment assembly that composes engine and Web API behavior.
This separation keeps the core small, the web surface modular, and the runtime acceptance-focused.
npm install @rxflex/rom
pip install rom-runtimePublished packages:
- npm:
@rxflex/romhttps://www.npmjs.com/package/@rxflex/rom - PyPI:
rom-runtimehttps://pypi.org/project/rom-runtime/
If you want package-specific usage examples, see:
cors_enabledisfalseby default, so cross-originfetch()is direct by default instead of browser-blocked.proxy_urlis optional and supportshttp://,socks5://, andsocks5h://.cookie_storeis optional, but the Node.js and Python wrappers now update it automatically so cookies survive separate bridge-backed calls.- For convenience, the Node.js and Python wrappers also accept
cookie_storeas a raw cookie header string and acookiesalias with string/object/array inputs, then normalize it into ROM's serialized cookie store. local_storageandsession_storageare optional and accept serialized JSON objects, JS/Python maps, or entry arrays in the wrappers; updated values are synced back into wrapper config after each call.referreris optional if you needdocument.referrerto match an upstream page.- Native Node.js and Python bindings keep one live runtime per
RomRuntimeinstance, so globals, cookies, and storage created in oneeval()call remain available to the next call. - The default navigator surface is Chrome-like rather than ROM-branded, including
navigator.userAgent,navigator.vendor, andnavigator.userAgentData.
cargo buildcargo testecho "{\"command\":\"surface-snapshot\"}" | cargo run -p rom-runtime --bin rom_bridgeimport { RomRuntime, hasNativeBinding } from "@rxflex/rom";
const runtime = new RomRuntime({
href: "https://example.test/",
cors_enabled: false,
proxy_url: process.env.ROM_PROXY_URL ?? null,
});
const result = await runtime.evalAsync(`
(async () => {
document.cookie = "seed=1; path=/";
const response = await fetch("https://example.test/data");
return JSON.stringify({
href: location.href,
status: response.status,
body: await response.text(),
cookie: document.cookie,
});
})()
`);
console.log("native binding:", hasNativeBinding());
console.log(JSON.parse(result));
await runtime.evalAsync(`
(async () => {
await fetch("https://example.test/next");
return document.cookie;
})()
`);
console.log("persisted cookie store:", Boolean(runtime.config.cookie_store));Optional native build:
cd bindings/gom-node
npm run build:nativefrom rom import RomRuntime, has_native_binding
runtime = RomRuntime(
{
"href": "https://example.test/",
"cors_enabled": False,
"proxy_url": None,
}
)
print("native binding:", has_native_binding())
result = runtime.eval_async(
"""
(async () => {
document.cookie = "seed=1; path=/";
const response = await fetch("https://example.test/data");
return JSON.stringify({
href: location.href,
status: response.status,
body: await response.text(),
cookie: document.cookie,
});
})()
"""
)
print(result)
runtime.eval_async(
"""
(async () => {
await fetch("https://example.test/next");
return document.cookie;
})()
"""
)
print("persisted cookie store:", bool(runtime.config.get("cookie_store")))Optional native build:
python -m pip install maturin
python -m maturin develop --manifest-path bindings/gom-python/Cargo.tomlnpm install
npx playwright install chromium
npm run fingerprintjs:browser-referenceROM does not treat compatibility as a vague goal. It already has two concrete acceptance layers:
- internal structured probes via
RomRuntime::surface_snapshot()andRomRuntime::fingerprint_probe() - a vendored FingerprintJS harness via
RomRuntime::run_fingerprintjs_harness()
That gives the project a measurable loop instead of an anecdotal one.
- push deeper Web Platform coverage without losing runtime clarity
- improve long-lived networking and worker fidelity
- keep tightening browser-like exception and validation behavior
- expand reference-driven compatibility checks
- strengthen the native JS/Python binding story on top of the shared bridge protocol
.
├── bindings/
│ ├── gom-node
│ └── gom-python
├── crates/
│ ├── rom-core
│ ├── rom-runtime
│ └── rom-webapi
├── docs/
├── fixtures/
└── tools/
ROM is experimental, but it is being built in the open with a clear technical direction. If you care about browser emulation, deterministic runtime design, anti-bot research tooling, or compatibility-first Web API implementation, this repo is meant to be inspectable and hackable.
Issues, design discussion, and focused contributions are welcome. Start with CONTRIBUTING.md and use SECURITY.md for vulnerability reports.
MIT. See LICENSE.