capsite/toybox: vats with code installed at runtime, on workerd
Each vat is a Durable Object that stores code by hash and runs it as a facet through the Worker Loader: an empty env, no fetch, storage that carries over upgrades. Two sockets with no configured address: whoever starts workerd hands them in (systemd socket activation), so the admin socket is the authority to install code. Tests run a real workerd: install, upgrade, isolation between vats, no fetch, the public port can't install, and code and state survive a crash. They spawn the package binary rather than the .bin shim, which forks workerd, so killing the shim orphaned it and the test run never ended. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1d8a220bbc
commit
88739d9495
12 files changed
+401
No files matched your search
@@ -0,0 +1,36 @@
|
||||
# toybox
|
||||
|
||||
Vats whose code is installed at runtime: each vat is a Durable Object whose app runs as a [facet](https://developers.cloudflare.com/dynamic-workers/usage/durable-object-facets/) on code loaded through workerd's Worker Loader. Think `vatAdmin.createVat(bundle)`, with a supervisor per vat.
|
||||
|
||||
- [supervisor.js](supervisor.js): `Vat` stores code by SHA-256 and runs the app on it; `Public` and `Admin` are the two entrypoints.
|
||||
- [config.capnp](config.capnp): the sockets have no addresses; whoever starts workerd hands them in, so holding a socket is the authority it grants.
|
||||
|
||||
| socket | reaches | what its holder can do |
|
||||
|---|---|---|
|
||||
| `http` | `Public` | `GET /<vat>/…`: talk to a vat's app |
|
||||
| `admin` | `Admin` | `PUT /<vat>` with an ES module body: install it and (re)start the app; its storage carries over |
|
||||
|
||||
An app must export `class App extends DurableObject` (see [apps/](apps/)). It gets an empty `env` and no `fetch`.
|
||||
|
||||
## Run it
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm test # a real workerd: install, upgrade, isolation, no fetch, crash and restart
|
||||
pnpm start # systemd-socket-activate on 127.0.0.1:8787 and ./admin.sock; state in ./state
|
||||
```
|
||||
|
||||
Then, in another terminal:
|
||||
|
||||
```sh
|
||||
curl -X PUT --unix-socket admin.sock --data-binary @apps/counter-v1.js http://toybox/alice
|
||||
curl localhost:8787/alice # v1 count 1
|
||||
curl -X PUT --unix-socket admin.sock --data-binary @apps/counter-v2.js http://toybox/alice
|
||||
curl localhost:8787/alice # v2 count 2
|
||||
```
|
||||
|
||||
## Under systemd
|
||||
|
||||
[systemd/](systemd/) has a socket and service pair, assuming this directory is installed at `/opt/toybox`. The service gets no network of its own (`PrivateNetwork=yes`): only the two sockets it is handed.
|
||||
|
||||
`PrivateNetwork=` needs the system manager; as a `systemd-run --user` unit, systemd logs "lack privileges for network namespace, proceeding without".
|
||||
Reference in new issue
Block a user