/** @file toybox supervisor: one Vat per name; its app runs as a facet on installed code. */ import { DurableObject, WorkerEntrypoint } from "cloudflare:workers"; const hex = (buf) => [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join(""); /** * Holds the vat's installed code and egress grant; the app facet holds the app's state. * * Code is stored by hash so the facet can be rebuilt after hibernation or a * restart. The app's global `fetch` throws; a granted host arrives as * `env.egress`, an {@link Egress} the app can pass along like any object. * * Each `start` begins a new incarnation, which names the loaded Worker: the * same code with a different grant must not share a Worker. */ export class Vat extends DurableObject { constructor(ctx, env) { super(ctx, env); ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS bundle (hash TEXT PRIMARY KEY, src TEXT)"); ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS current (k INTEGER PRIMARY KEY CHECK (k = 0), hash TEXT)"); ctx.storage.sql.exec( "CREATE TABLE IF NOT EXISTS incarnation (k INTEGER PRIMARY KEY CHECK (k = 0), n INTEGER, egress TEXT)", ); } /** * Install `src` and run the app on it; the app's storage carries over. * * @param {string} src an ES module exporting `class App extends DurableObject` * @param {string | null} egress the one host (`name[:port]`) the app may fetch, or none * @returns {Promise} the code's SHA-256, hex */ async start(src, egress) { const hash = hex(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(src))); const { sql } = this.ctx.storage; sql.exec("INSERT OR IGNORE INTO bundle VALUES (?, ?)", hash, src); sql.exec("INSERT OR REPLACE INTO current VALUES (0, ?)", hash); const [prev] = sql.exec("SELECT n FROM incarnation").toArray(); sql.exec("INSERT OR REPLACE INTO incarnation VALUES (0, ?, ?)", (prev?.n ?? 0) + 1, egress); this.ctx.facets.abort("app", new Error("upgraded")); return hash; } #app({ hash, src, n, egress }) { return this.ctx.facets.get("app", () => { const worker = this.env.LOADER.get(`${this.ctx.id}:${n}`, () => ({ compatibilityDate: "2026-10-01", mainModule: "app.js", modules: { "app.js": src }, env: egress ? { egress: this.ctx.exports.Egress({ props: { host: egress } }) } : {}, globalOutbound: null, })); return { class: worker.getDurableObjectClass("App") }; }); } fetch(request) { const [row] = this.ctx.storage.sql .exec( `SELECT b.hash, b.src, coalesce(i.n, 0) AS n, i.egress FROM current c JOIN bundle b USING (hash) LEFT JOIN incarnation i ON i.k = 0`, ) .toArray(); if (!row) return new Response("no code installed\n", { status: 404 }); return this.#app(row).fetch(request); } } /** * A grant of one host: `fetch`, WebSocket upgrades included, reaches the * switchboard only for the host in `ctx.props`, which no holder can change. */ export class Egress extends WorkerEntrypoint { fetch(request) { const { host } = new URL(request.url); if (host !== this.ctx.props.host) return new Response(`egress to ${host} not granted\n`, { status: 403 }); return this.env.SWITCHBOARD.fetch(request); } } const vatOf = (env, url) => env.VATS.getByName(new URL(url).pathname.split("/")[1]); /** The http socket: `//...` goes to that vat's app. */ export class Public extends WorkerEntrypoint { fetch(request) { return vatOf(this.env, request.url).fetch(request); } } /** * The admin socket: `PUT /` with an ES module body installs and starts * it; `?egress=` grants it that one host. */ export class Admin extends WorkerEntrypoint { async fetch(request) { if (request.method !== "PUT") return new Response("PUT code to /\n", { status: 405 }); const egress = new URL(request.url).searchParams.get("egress"); return new Response((await vatOf(this.env, request.url).start(await request.text(), egress)) + "\n"); } } export default Public;