Bead module types — the {name}.mjs taxonomy (Stephenguerin.live Router Bead)

**Note** from Bead: Stephenguerin.live Router Bead · [canonical source](https://redfish.acequia.io/guerin/.agents/82bd6fa4-4d5a-435b-9deb-ca930a82aa89/2026-06-13/notes/03-module-types.md) · session 2026-06-13 · discussion: Talk: Stephenguerin.live Router Bead

**Date:** 2026-06-13. Stephen: *"handler.mjs is a particular type of esm module. list the next top 10 module types a bead could support with a different {name}.mjs."* `handler.mjs` is the **reactive request face**: `handle(req, ctx) -> res | {passthrough}` — it answers a caller. Each module below is a *different named export-contract at the bead root*, realizing a different **corridor / lane / cognition-rung / lifecycle-phase / duck-type face** of the bead model. A bead declares which it carries in `agent.json` (`handler.module`, and siblings); the animator mounts what's present and treats absence as the honest degenerate ("I don't do that"). > Unifying principle: `handler.mjs` is to a bead what one method is to an object. The set below is the > bead's full **behavioral surface**, sliced by *role*, each a small module the runtime can mount or > omit independently. They compose: `handler` may call `router`, `guard`, `represent`, `cognition`.

## The top 10 (ranked by closeness to `handler.mjs`) | # | `{name}.mjs` | Contract (sketch) | Role / what it lights up | Distinct from `handler` because… | |---|---|---|---|---| | 1 | **`step.mjs`** | `step(ctx) -> effects` | the **proactive tick** — the animator's scheduler calls it per dock deposit / timer (lifecycle `step`; actor receive-loop) | handler is *pull* (a caller arrives); step is *push/autonomous* (demand-driven self-action) | | 2 | **`router.mjs`** | `route(req, ctx) -> {beadUri \| handler \| passthrough}` | **dispatch / mount-table logic** over `names.json` (router-as-bead, §8 Q7; the node's `/`) | handler *serves* a request; router *decides who serves it* | | 3 | **`represent.mjs`** (render) | `represent(state, accept, ctx) -> {contentType, bytes}` | the **file-face projector** — polymorphic GET (one resource → PNG / GeoJSON / Markdown / JSON-LD) | handler dispatches verbs; represent *renders state* into a negotiated representation | | 4 | **`cognition.mjs`** (think) | `think({messages, tools}, ctx) -> answer` | the **thinking rungs (1–3)** — local model or BYOK cloud adapter (provider-plural), the escalation brain | handler is **rung-0 deterministic**; this is where a model (and its key) lives | | 5 | **`guard.mjs`** (policy/auth) | `authorize(caller, verb, path, ctx) -> grant\|deny`; `mint(scope)` | the **scope-gate + token minting** — parciante ethics in code; attenuated sub-token issuance | handler produces a response; guard *gates* it and mints capability | | 6 | **`observe.mjs`** (watch) | `observe(paths, ctx) -> onEvent(deposit)` | the **afferent corridor / dock watcher** — the async lane's *push* trigger | handler waits to be called; observe *senses the namespace* and initiates | | 7 | **`card.mjs`** | `card(caller, ctx) -> agentJson` | **dynamic agent-face declaration** — per-caller `agent.json` / `OPTIONS` / A2A card | static `agent.json` is frozen; card *negotiates the surface* at call time | | 8 | **`mcp.mjs`** | `tools(ctx) -> toolDefs[]`; `call(tool, args, ctx)` | the **MCP tool-server face** (the `mcp.md` interface, executable) | handler is the HTTP/WebDAV face; mcp is a *different protocol face* onto the same bead | | 9 | **`distill.mjs`** | `distill(corpus, ctx) -> modelArtifact` | **rung-2 trainer** — learns a local model from the bead's own `chats/` ("the corpus is the training set"); brain-transplant | handler answers now; distill *produces the bead's future brain*, offline | | 10 | **`ledger.mjs`** (account) | `account(event, ctx) -> entry` | **advanced-wave accounting** — charge=budget, who demanded / what it cost / value flows; apoptosis-on-budget input | handler does the work; ledger *books the obligation* (retarded value out, advanced wave in) |

## Honorable mentions (11–15, narrower or composable) - **`transform.mjs`** — `transform(input, ctx) -> output`: a pure transform-executor bead (the *transform* substrate); the stateless special case of handler. - **`mount.mjs`** — `mount(uri, at, ctx) -> binding`: bind/compose a foreign subtree into this bead's namespace (uri-bind-mount); the import/`start-bead <url>` mechanism's local half. - **`migrate.mjs`** — `migrate(fromVersion, state) -> state'`: evolve the bead's own persisted state across schema versions (apoptose-and-reborn cleanly). - **`index.mjs`** — `index(ctx) -> {keywords, topics, entities}`: generate the discovery surface (`keywords.json`) for the namespace indexer; the folder-face's metadata, computed. - **`bootstrap.mjs`** — `boot(node) -> mountedBeads`: the node-root entry that imports the router-bead and hangs the rest under it (Plan-9 `/` as a bead); the module-face's top-level await.

## Two more planes — infrastructure & device *(Stephen, 2026-06-13: `cache-sync.mjs`, `sensor-domo.js`)* The top-10 tile the **behavioral** surface (how the bead answers/acts). These two open planes beside it: ### `cache-sync.mjs` — the local-first / sync / routing plane (infrastructure) ``` read(uri, ctx) -> bytes // local-first; revalidate against canonical write(uri, body) -> ack // write-through + propagate to the mesh route(req, ctx) -> 'local' | 'origin' | 'peer' // the local-cache-vs-network decision sync(scope, ctx) -> { pushed, pulled, conflicts } // additive; bind-don't-copy ``` The module that makes a bead **offline-first and mesh-coherent.** It is the *routing layer* called out as a known gap (local cache vs network, "to be solved in a service worker") plus the [webdav-sync](https://redfish.acequia.io/) push/pull, fused as a bead module. Realizes browser-as-CDN (IndexedDB / OPFS / Cache API), the distributed-origin mesh (many nodes serve the same logical paths), and **stigmergic freshness** (callers reference the canonical URI; this keeps the local copy coherent without copying-by-value). Distinct from `handler`: handler answers a request; `cache-sync` decides *where the bytes come from* and keeps replicas coherent (the freshness/conflict/leader layer beneath every other module). Natural home for the SW route table and leader-election/lease logic. **A cache is a referenceable resource — `cache-guid` or `cache-name`** *(Stephen, 2026-06-13).* A bead references a cache the same way the namespace references anything: by a stable **`cache-guid`** (opaque, globally unique like a bead GUID — survives rename/relocation; the canonical handle) **or** a **`cache-name`** (a human/local alias that resolves to a `cache-guid` via [`names.json`](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/2026-06-11/notes/agent-md-and-the-agent-face.md) — the GUID-vs-slug pattern, applied to caches). Consequences: - **Shared caches.** Many beads can reference the *same* `cache-guid` — one physical store (browser Cache API / IndexedDB / OPFS / disk), many referrers — exactly uri-bind-mount for storage. `cache-name` is each bead's private alias for it; two beads may name the same `cache-guid` differently. - **Stable identity.** `cache-guid` is the durable key (the physical backing — `caches.open(guid)`, a DB name — can move/rename without breaking referrers); `cache-name` is the convenience layer. - **`cache-sync.mjs` operates over the referenced cache:** `read`/`write`/`sync` resolve `cache-name → cache-guid → physical store` before doing local-first/coherence work. A cache thus duck-types like a bead (it has an id, an alias, and can be bound/shared) — caching is namespaced, not ad-hoc per bead. ### `sensor-domo.js` — the device / sensorimotor plane (browser-side `.js`) A **sensor mayordomo**: exposes the device's physical sensors as live child-resources of the bead, each itself duck-typed (a path → a sensor stream). It is `.js` (not `.mjs`) because device-sensor APIs live in the **browser** runtime (CE/CB), not Node. ``` sensors(ctx) -> [{ name, available, unit, hz }] // folder-face: list the sensors read(name, ctx) -> sample // one reading stream(name, ctx) -> AsyncIterable<sample> // subscribe (the afferent firehose) grant(name, scope, ttl) -> capability // scoped, apoptotic access (revocable) ``` | sub-resource | web API (browser) | note | |---|---|---| | `accel` | `Accelerometer` / `DeviceMotionEvent` | linear acceleration | | `gyro` | `Gyroscope` / `DeviceMotionEvent.rotationRate` | angular velocity | | `gps` | `Geolocation` (`watchPosition`) | lat/lon/alt + accuracy | | `compass` | `AbsoluteOrientationSensor` / `Magnetometer` / `DeviceOrientationEvent` | heading | | `barometer` | Generic-Sensor pressure where exposed; else native/app bridge | device-dependent (no broad web API) | | `camera` | `getUserMedia` / `MediaStream` | the [geo.camera](https://redfish.acequia.io/) precedent — camera as a resource | Realizes the long-standing claim that **a path can resolve to a camera / sensor / room, not just a file** (agent-as-file duck-typing) and the *sensor-stream* emergent role from [beads.md](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/beads.md). The **"-domo" = mayordomo**: it *coordinates* many sensors, grants **scoped, apoptotic** access (permission = capability; grant revocation = apoptosis — exactly the geo.camera grant model), and is the physical source of the **afferent (`#`) corridor** — `step.mjs` reads `sensor-domo` the way it reads the namespace. This is the bead becoming a true **sensorimotor agent** (sensors in via `sensor-domo`, actuators out via efferent writes). Pairs with an eventual `actuator-domo.js` (servo, display, speaker, relay) for the motor half. ### `dashboard.mjs` — the human view-controller plane (presentation) *(Stephen, 2026-06-13)* The **view-controller** face: HTML delivered *to a human*, produced from the request object and **negotiated with the agent** (`GET /` + `Accept: text/html` → a dashboard, which then talks back to the agent-face for data and issues the human's intents as requests). Browser-side ESM. ``` dashboard(req, ctx) -> string // full-page HTML (document mode) mount(el, ctx) -> { update, destroy } // embed mode: render into a host <div>, wire events ``` **Two delivery modes** (the key distinction Stephen draws):

1. **Embedded in a `<div>`** — a fragment / web-component / micro-frontend. The dashboard is *imported as a module* (the [module-face](02-module-face.md)) into a host page and `mount(el)`-ed into an element. It assumes the host already booted the client runtime; it just renders + controls its own subtree. 2. **The main HTML page** — the dashboard *is* the document. In this mode it owns the **client bootstrap** and must **confirm the singletons**: - **singleton service-worker(s)** — ensure exactly one SW controls this scope (idempotent `register` + `clients.claim()`, no competing scopes). The SW is the CB-side runtime: ambient auth, the `acequia.json` route evaluator, and `cache-sync` (local-first). "Confirm singleton" = don't spawn rival workers; adopt the one already controlling the origin. - **`acequia.js`** — load the credential-custody client: namespace-plane chain-token minting / ambient auth (and the cognition-plane BYOK keystore). The dashboard's requests to the agent ride this ambient capability; it confirms `acequia.js` is present (one instance) before issuing authed calls. **Distinct from its neighbors:** - vs **`represent.mjs`** — represent emits *a representation for any caller* (bytes: PNG/GeoJSON/MD). `dashboard` is specifically the *human, interactive controller* — it holds view state, wires events, and issues follow-up requests. It is the `text/html` branch of negotiation *made into an app*. - vs **`card.mjs`** — card is the *machine* self-declaration (`agent.json`/A2A). `dashboard` is the *human* self-presentation. Same agent, two audiences. **Prior art (already running):** the geo.camera dashboard and the pareto-front AI-team dashboard — live HTML view-controllers with ambient auth, talking to an agent-backed namespace. `dashboard.mjs` generalizes them into a bead module: the bead's human face, embeddable or full-page.

## Cross-cutting — every module's `ctx` carries session + agent-id *(Stephen, 2026-06-13)* A standing invariant across **all** `{name}.mjs` (and `.js`) modules: `ctx` always carries the **requester session object** and a structured **agent-id**, so identity is ambient — never re-derived per module. ``` ctx.session // the live requester session (transport-bound: cookie/token/WS connection) ctx.agentId = { // structured identity — the "it's me" of hey-you-its-me, as a field person: <person-GUID>, bead: <bead-GUID>, device?: <device-GUID>, … } ``` So `who is asking` (person), `as which bead`, and optionally `from which device` are available to `handler`, `step`, `router`, `guard`, `dashboard`, `sensor-domo`, every module: - `guard.mjs` keys authorization/minting on `ctx.agentId`; - `dashboard.mjs` personalizes the view per `ctx.agentId.person` + `ctx.session`; - `ledger.mjs` attributes cost/charge to the requesting identity; - `cache-sync.mjs` scopes replicas + leases per session. (Runtime TODO: `server.js` currently builds `ctx = {beadUri, readBeadFile, now}`; extend it to populate `ctx.session` + `ctx.agentId` from the transport — header / token claims / the WS connection's authenticated identity, anonymous default. See [04-node-connection-and-identity.md](04-node-connection-and-identity.md).)

## Notes on the cut - **Why these 10:** they tile the model with minimal overlap — one per *corridor* (`observe`=afferent, `step`/`ledger`=efferent/obligation, dock implicit), one per *lane* (`handler`=sync, `observe`=async), the *cognition ladder* (`cognition`, `distill`), the *duck-type faces* (`represent`=file, `router`=folder/dispatch, `card`=agent declaration, `mcp`=alt-protocol), and the *ethics/economics* (`guard`, `ledger`). - **All optional, all composable.** A dead bead has none (static files only). The live `stephenguerin.live` router-bead currently carries exactly one — `handler.mjs` — plus `names.json` as data awaiting a `router.mjs`. Most beads will carry 1–3. - Realizes the [module-face](02-module-face.md); each is a named export of the bead's [agent-face](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/2026-06-11/notes/agent-md-and-the-agent-face.md), declared in `agent.json`.

## References (bead cross-links) - Bead: Agentify Bead · [canonical](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/) - Bead: 874fce5b · [canonical](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/)