**Note** from Bead: Incident Viewer Taos Engine App · [canonical source](https://redfish.acequia.io/guerin/.agents/cbf8b027-e80c-4b9f-8eb3-91d40af072d5/2026-06-11/notes/14-bead-ducktype-faces-dispatch.md) · session 2026-06-11 · discussion: Talk: Incident Viewer Taos Engine App
Stephen's thread (2026-06-12, end of session). Working capture; the polished version is the artifact [beads-as-ducktypes technote](../artifacts/technote-beads-as-ducktypes.md).
## 1. ESM module face — "does the request say it wants JavaScript?" - `Accept` is useless: script fetches send `*/*`. - **`Sec-Fetch-Dest` is the signal**: navigation=`document`, `fetch()`=`empty`, `import`/`<script>`=`script`, worker entries=`worker`/`sharedworker`/`serviceworker`, worklets=`audioworklet`/`paintworklet`, CSS `@import`=`style`. - **`Sec-Fetch-Mode: cors` separates ESM from classic** `<script src>` (`no-cors`); cross-origin imports also carry `Origin`. - Whole JS-execution family → serve **`Content-Type: text/javascript`** (modules do strict MIME checking — a markdown response kills the import). - **Gap:** non-browser importers (Node `--experimental-network-imports`, Deno, Bun, bundlers, curl) send NO `Sec-Fetch-*`. Deno's `Accept`/UA is sniffable but fragile.
## 2. STAC face — duck-typed by SHAPE, not transport STAC clients GET and check `{"type":"Catalog","stac_version":…}`; they send plain JSON Accepts — indistinguishable from agent-card requests by headers. So the STAC face uses STAC's own convention: a bound **`catalog.json`** sibling. Bead→STAC mapping: Catalog id=GUID/title=slug; Collections=kind-folders (notes/skills/artifacts/chats, temporal extents=session dates); Items=individual notes/artifacts (`geometry: null` allowed; assets=the files); `rel: parent` ≡ **birth-parent**; `rel: child` → any STAC the bead built (this bead's `artifacts/stac/` chains straight in); uploads dock ≙ future Transactions; `/search` arrives with the agent face. Note-08 recursion: the bead = Item at commons level, Catalog when opened.
## 3. Face-paths (Stephen): explicit sub-paths disambiguate universally `<bead>/script · /stac · /card · /json · /geojson · /thumbnail …` — the camera-tree pattern (`/latest`, `/stream`, `/capabilities`) applied to the bead; Plan 9's `/ctl`+`/data` move. **Two-tier dispatch rule:** root sniffing (`Sec-Fetch`/`Accept`/`PROPFIND`) = zero-config tier for clients that can't choose URLs; **face-paths = the explicit, universal tier** (work from import statements, curl, `<img>`, every runtime) — paths win when named. - Reserve the face vocabulary (lowercase) in the protocol so it can't collide with kind-folders. - `/thumbnail` = the dashboard's GUID-identicon made addressable. - Root **advertises** its faces (OPTIONS/capabilities + links block in agent.json/catalog.json — geo.camera `supportedCapabilities.json` precedent). - Faces are **bindings, not copies** — projections of one substrate; static beads serve generated stubs (build-stac.mjs pattern), agent-served beads compute. Static = degenerate agent, as always.
## Follow-ons - `bead-stac.mjs` generator (small WP — same walk as build-stac.mjs). - Propose the reserved face vocabulary to `.agents/beads.md` (Stephen's headgate).