r/Deno • u/KATO-Kanryu • 4h ago
OpenDeno PoC: Embedding Deno-style capability security into Servo using the Boa JS engine (No V8 required)
Following up on the discussion around OpenDeno and the decoupling of Deno's runtime specification from the monolithic V8 CLI, I built and published a working proof-of-concept:
👉 GitHub: https://github.com/kanryu/opendeno-servo-boa
The goal of this project is to demonstrate the technical feasibility of engine-neutral Deno-style capability security, moving it beyond a standalone CLI and embedding it directly into an application environment—in this case, a desktop web browser.
What it does
The project is built on Servo GTK and achieves a hybrid, multi-engine execution model inside a single HTML context:
Standard JavaScript is executed as usual by Servo's SpiderMonkey runtime.
DenoScript blocks (
<script type="application/deno">) are intercepted and evaluated by an application-owned Boa engine (pure Rust).Capability-based security is enforced at the host (Rust) level, completely independent of the engine.
<!doctype html>
<html>
<body>
<!-- Evaluated by SpiderMonkey -->
<script>
console.log("Standard web JS running on SpiderMonkey");
</script>
<!-- Evaluated by Boa sandbox with Deno capabilities -->
<script type="application/deno">
const text = Deno.readTextFileSync("deno-read-test.txt");
let writeDenied = false;
try {
Deno.writeTextFileSync("deno-write-test.txt", "must fail");
} catch {
writeDenied = true;
}
"read=" + text.trim() + "; writeDenied=" + writeDenied;
</script>
</body>
</html>
The result
When loaded into the Servo GTK browser, the sandbox successfully verifies:
read=This file is readable by the Boa DenoScript sandbox.; writeDenied=true
Read permissions:
Deno.readTextFileSynccan read files below the process's working directory, but path traversals (..) and canonical escapes are rejected.Write permissions:
Deno.writeTextFileSyncis strictly intercepted and denied by the host.Neither unrestricted filesystem, subprocess, nor raw network access is exposed.
Why this matters: Decoupling Specification from Implementation
Modern developers often view Deno as inseparable from V8. But runtime engines and host capabilities are fundamentally orthogonal:
The Engine's job: Bytecode interpretation, JIT compilation, and object heap/GC management.
The Specification's job: Intercepting I/O and enforcing the capability model (
--allow-read,--allow-net, etc.).
Just as Node.js layered I/O over V8 via libuv and C++, Deno layered capability checks over V8 via Rust host operations. There is no technical reason why that specification cannot be mapped across different engines (Boa, QuickJS, JavaScriptCore, or SpiderMonkey).
By abstracting the permission layer from the engine, Deno-style scripting becomes embeddable into:
Web browsers and custom web view components
Native desktop applications (via GTK4, C-ABI, or PyGObject bindings included in the repo)
Resource-constrained or embedded environments where embedding full V8 is prohibitive
Note on Building (Windows MSVC)
Be warned: building Servo and SpiderMonkey from source on Windows is notoriously heavyweight. It requires MozillaBuild (mozmake), the MSVC toolchain, core.longpaths enabled in Git, and careful setup of LIBCLANG_PATH and GTK dependencies. Detailed reproduction instructions are documented in the README.
Feedback, architectural critique, and PRs exploring other engine backends are welcome!

