capsite/toybox: egress through Flower-style switchboard handles
The switchboard now serves a Cap'n Web Handle on each of its sockets:
public (no loopback or private addresses) and local. A socket's labels
are fixed in config, so holding the socket's fd is holding that handle;
constrain(labels) returns a narrower one, as Flower's Constrain returns
a narrower fd. Labels only accumulate: a missing key matches anything,
and a key both sides set must agree (labels.js, shared by both).
The switchboard enforces the labels; toybox's Egress no longer checks
URLs itself. It opens a session to its one switchboard socket, constrains
the handle to the vat's grant, and forwards through it; a WebSocket
crosses the session as a pair of message streams. A vat narrows its own
grant with env.egress.constrain().
Grants are now labels as JSON. One stored before, a bare host[:port],
reads as {host, port}.
pnpm start and the systemd units give toybox the public handle; the
switchboard unit binds the local one in its own 0700 runtime directory.
The tests give their toybox the local handle to reach an echo server,
and check that a toybox holding only the public one never reaches it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
2820c015ee
commit
f0557d29d6
14 files changed
+337
-92
No files matched your search
@@ -1,13 +1,15 @@
|
||||
import { DurableObject } from "cloudflare:workers";
|
||||
|
||||
/** `?url=<target>` fetches it through `env.egress`; with `&ws=1`, opens a WebSocket there and reports the echo of "hello". */
|
||||
/** `?url=<target>` fetches it through `env.egress` (narrowed by `&constrain=<labels as JSON>`, if given); with `&ws=1`, opens a WebSocket there and reports the echo of "hello". */
|
||||
export class App extends DurableObject {
|
||||
async fetch(request) {
|
||||
const params = new URL(request.url).searchParams;
|
||||
const target = params.get("url");
|
||||
const { egress } = this.env;
|
||||
if (!egress) return new Response("no egress granted\n");
|
||||
if (!this.env.egress) return new Response("no egress granted\n");
|
||||
try {
|
||||
const egress = params.has("constrain")
|
||||
? await this.env.egress.constrain(JSON.parse(params.get("constrain")))
|
||||
: this.env.egress;
|
||||
if (!params.has("ws")) return new Response(`got: ${await (await egress.fetch(target)).text()}\n`);
|
||||
const resp = await egress.fetch(target, { headers: { Upgrade: "websocket" } });
|
||||
const ws = resp.webSocket;
|
||||
|
||||
Reference in new issue
Block a user