# Host modules

A host module is one bridge between the Wasm userspace and the browser, in one
directory. Each `NAME/module.json` names every file of its module. A page has
the modules its embedding names, by the URLs of their manifests: the registry
([`modules.mjs`](modules.mjs), `hostRegistry`) loads those and no other, on the
page and in the Worker. [`manifests.mjs`](manifests.mjs) lists the modules this
repository builds; the build, packaging and ABI tests read those manifests, and
a page of the site names those its route uses ([`site.mjs`](../src/site.mjs)).

```json
{ "name": "http", "version": 0, "dependencies": ["runtime@0"], "phase": "kernel",
  "imports": ["env.dolly_http_dispatch"], "host": "http.mjs",
  "contracts": ["dolly-http-0.wat"], "process": [], "headers": ["http.h", "http-abi.h"],
  "kernel": ["kernel.c"], "client": ["client.c"] }
```

| Field | Meaning |
| --- | --- |
| `name`, `version` | The requirement `name@version` that images (`REQUIRES HOST`) and executables (`dolly.host` records) name |
| `gives` | One line of what the module gives a session, as `nena` lists it |
| `fixed` | Why the module is never enabled or disabled after boot (below); absent when `nena@0` may switch it |
| `provides` | `"kernel"` on a runtime (below); absent otherwise |
| `switches` | `true` on the one module whose page side is given the registry's `list`, `enable` and `disable` (`nena`); absent otherwise |
| `dependencies` | Modules this one uses through `get(name)` |
| `phase` | When the Worker side starts: after the kernel loads (`kernel`) or after the image is restored (`image`) |
| `imports` | Kernel Wasm imports this module provides; `abi/dolly-browser-0.wat` is their reviewed allowlist |
| `host` | Trusted JavaScript: `check()`, `browser()` and `worker()` sides ([`modules.mjs`](modules.mjs)); a module with a client exports the `digest` it implements |
| `processWorker` | Trusted JavaScript served inside the process Worker, bundled as `dist/dolly-process-NAME.mjs`; `dso` and `stack` have one (below) |
| `contracts` | WAT of the kernel boundary: its exports are kernel exports, its imports the host's |
| `process` | WAT of process-side ABIs, such as the threads entry point |
| `headers` | C API and packets, installed as `<dolly/NAME.h>`; `NAME-abi.h` is generated from the WAT constants and carries the module's ABI digest |
| `kernel` | Kernel C; defines `dolly_NAME_kernel`, the process operations it handles and its release hook ([`process-kernel.h`](../src/process-kernel.h)) |
| `client` | Process C linked as `libdolly-NAME.a`; it records `DOLLY_HOST_REQUIRE(NAME, VERSION, DOLLY_NAME_ABI_DIGEST)` |
| `tools` | Process C of a program a package builds from the module's directory, such as `stack-wrap`; optional |
| `package` | The package whose recipe installs the module's headers and clients, which the seed then leaves out; absent when the seed holds them |

A provider's `browser()` and `worker()` receive the page's shared resources
(`mount`, `canvas`, `keyboard`, `applicationBase` (the image directory), `site`
(the root of the site's routes, when the embedding has one), `showStatus`,
`fatal` and `indicators` (`hold(holder, held)` and `show()`, the page's corner
indicators)), `send` to the other side, `ask` (a message whose handler's
result, or error, comes back), `get(dependency)` and its `configuration`; `check()` receives the page's resources too, and on a switch
after boot the `signal` that is aborted once the registry stops waiting.
The page side holds no view of kernel memory ([browser boundary](../docs/browser-boundary.md)):
the Worker side owns the module's mailboxes and sends the page copies, and the
page side keeps its DOM work and sends what the user does.
`get("runtime")` on the Worker side gives the kernel's `memory`,
`serviceOnNotify(words, index)`, which resumes waiting programs each time a
mailbox word is notified, `leaseBridge(lease, mailbox, send)` for a device
leased per process ([`runtime/lease-bridge.mjs`](runtime/lease-bridge.mjs)) and
`kernelPlugin(bytes, kernel)` for a resident library linked into the
kernel. A device module imports only its own files, so it can be delivered on
its own: its generated `abi.mjs` carries its contracts' constants and the
process contract's errno numbers, as its C includes `<dolly/process.h>`. The
modules that are the runtime's own machinery (`runtime`, `http`, `threads`,
`dso`, `stack`, `nena`, `snapshot`, `packages`, `build`) also use the runtime's files. They return an instance whose optional members the registry
calls: `start` (Worker/page handshake), `messages`, `bindings` (kernel
imports), `service` (periodic work), `imageRestored(context)` (after the system
image is restored, before image-phase starts), `claimsKey(event)` (take a key
from the guest's input), `surfaceSize`, `entryStarted(context)` (the image ENTRY may
now run), `ended()` (the links, `{ text, href }`, it offers on the page's
notice once the image has ended), `dispose` and `disabled` (below); and whose optional records
the registry assembles:
`page` (members of `window.__dolly`, by descriptor), `builder` (the module's
configuration for a child build host, `host.builder`) and `inherited` (what an
opened result tab or restored session inherits, `host.inherited`). A module may
also export `boot(route)`: at most one selects what a route boots, returning the
image, a bootstrap label, the custom image record it restores and its own
configuration, as the snapshot module does for `/session/`.

A module's operations and packets are its own: op numbers are globals in its WAT
contract, packets are in its header. `DOLLY_NAME_ABI_DIGEST` is the SHA-256 of
the exact bytes of its WAT contracts and other headers, so any edit to them
changes the identity its client stamps. The loader refuses an executable whose
digest differs from the provider's, as it refuses a wrong `dolly.process` stamp
([`requirements.mjs`](requirements.mjs)).

Adding a module takes its directory, one name in `manifests.mjs`, its imports
in `abi/dolly-browser-0.wat` and its row in
[browser boundary](../docs/browser-boundary.md); the last two are where a human
reviews the authority it adds. Its header and client reach programs through a
package, `Dollyfile-libdolly-NAME` named as the manifest's `package`, which
`scripts/build.sh` then leaves out of the seed, as for gpu, audio, microphone,
sockets and touch (stack's is `stack-wrap`); a module that a
core source includes stays in the seed. A module without imports may instead own a
reserved origin of the HTTP broker ([`http/local-services.mjs`](http/local-services.mjs)),
as `build` and `packages` do: its service admits each request itself.

## Modules switched at run time

`nena@0` lets the session of an image that declares it enable and disable
modules ([process model](../docs/process-model.md#host-modules-at-run-time)).
Its page side alone is given the registry's `list`, `enable` and `disable`
([`modules.mjs`](modules.mjs)); the page attaches or detaches its side and the
Worker's follows by message, starting the provider with the context of boot.
A module that can be switched therefore keeps to this: `browser()` and
`worker()` may run again after `dispose()`, which lets go of what the side
holds of the page (DOM, listeners, Workers); `disabled()` follows it when the
module is switched off and the runtime goes on, and takes back what the
module gave the runtime or a dependency (a registered service, the thread
provider). An import asks at each call whether its module is enabled. A
module that cannot keep to this (it consumes page state once, or what it
starts cannot start twice) says why in `fixed`.

## Modules served in the process Worker

`dso@0` (the loader behind `dlopen` and the FFI dispatcher) needs one
process's instance and function table, not the kernel, so its operations are
answered in that process's own Worker. Its Worker side fetches the bundle
once at boot and registers it with the runtime (`serveInProcess`); the
supervisor puts it in the start message of exactly the executables that
record the module, and their Worker imports it before `_start`
([`process-worker.mjs`](../src/process-worker.mjs)). Every other Worker loads
none of it, in an image that declares the module too, so declaring it costs a
program that does not record it nothing. An executable records `dso@0` when it
links a member of `libdolly-dso.a`: `cc -rdynamic` selects the loader's client,
a call to `dolly_ffi_*` the FFI client. A program that only calls `dlopen`
gets the process libc's refusal and records nothing. In a program using
`threads@0` it serves every thread's Worker, each with a function table of its
own: a call works in any thread, and loading a library or preparing a closure,
which would add an entry to one Worker's table only, is refused (`ENOTSUP`).

`stack@0` lets a program recurse deeper than one browser stack
([browser stack](../docs/browser-stack.md)). An executable records it once
`stack-wrap` has rewritten it (`Dollyfile-stack-wrap`, which holds the header
the seed leaves out): each function that makes calls charges its frame against
the program's `dolly_stack_room`, and one that finds too little room calls
itself again through `DOLLY_STACK_CONTINUE`. The provider answers in the
process's Worker by running the program's `dolly_stack_continue` on a fresh
stack, and sets each stack's room. It serves every thread's Worker of a
program using `threads@0`.

## Local sockets

`sockets@0` has no import, no page or Worker side and no browser authority.
Its kernel side is a descriptor kind of the runtime's table
([`process-kernel.h`](../src/process-kernel.h)). Its client defines the
libc's socket calls, which many programs reference for a network path they
never take here, so `cc` does not link it with the other clients: a program
asks with `-ldolly-sockets`
([process model](../docs/process-model.md#local-sockets)). That argument
comes before the archives `cc` adds, and the linker takes each name from the
first archive that defines it; every other program gets libc's refusals
([`socket-refusals.c`](../src/process/socket-refusals.c), an object that
names the same functions and nothing else) and records nothing.

## Pseudo-terminals

`pty@0` has no import and no page side either: its kernel side is a second
kind of the descriptor that sockets are, and the supervisor delivers the
signals a pseudo-terminal raises for the programs on its slave
([process model](../docs/process-model.md#pseudo-terminals)). Its client
defines the libc's `openpty`, so a program asks with `-ldolly-pty` as one
asks for sockets. The libc itself forwards `ioctl(TIOCSWINSZ)` on a master.

## Runtimes

A runtime is a module whose manifest says `"provides": "kernel"`; `runtime@0`
is the one this checkout ships, and nothing else about it is special. Every
image declares exactly one with the line of any module (`REQUIRES HOST
runtime@0`). The lint, the page and a build host find it through that field,
never through its name, and a module that others merely depend on is not
thereby declared. A runtime provides:

- the process ABI (`process`: [`dolly-process-0.wat`](../abi/dolly-process-0.wat),
  the gate contract, `process.h`) that its executables compile against;
- the supervisor and image contracts (`contracts`:
  [`dolly-supervisor-0.wat`](../abi/dolly-supervisor-0.wat),
  [`dolly-image-0.wat`](../abi/dolly-image-0.wat)): spawn, wait, signals, the
  terminal mailbox, and the snapshot format of every image and package built
  on it;
- the kernel (`kernel`: its sources link as the kernel itself, not as a
  `dolly_NAME_kernel` module) and its outer `imports`, bound through
  `bindings` like any module's;
- a Worker instance with `memory` and `supervisor(dolly)`, which the registry
  hands out as `host.kernel`;
- the seed (`dist/dolly.data`, a snapshot in the image format: the compiler,
  libc adapter and bootstrap commands built for its process ABI), which a
  build restores when a recipe has no `FROM`.

Images and packages record their runtime as they record any module: the line
goes into the artifact and its receipt. The page refuses an image whose
runtime it does not provide before ENTRY, naming it; a build refuses a recipe
without one, naming the line to add; `INSTALL` and `amy` refuse a package
whose runtime the installing image does not declare.

Executables carry no `dolly.host` record for their runtime. What an executable
speaks to a runtime is the process contract, and its `dolly.process` stamp is
the identity of exactly that: the typed interface and the bytes of
`process.h`, checked on every executable before instantiation, with or without
a libc ([machine contracts](../abi/README.md)). An executable built for a
runtime with another process contract is refused by that check; runtimes that
implement the same contract run the same executables.

[`worker-image.mjs`](../src/worker-image.mjs) and the process loader still
load this runtime's kernel, seed and process contract by fixed path; a second
runtime needs its provider to name them.
