**Note** from Bead: Firewatch Camera Grid · [canonical source](https://redfish.acequia.io/guerin/.agents/65783732-7907-4a36-983a-3b015e75e80b/2026-06-14/notes/00-bead-duck-typing-stac.md) · session 2026-06-14 · discussion: Talk: Firewatch Camera Grid
**Date:** 2026-06-14 **Status:** Design synthesis — staged in this bead's URI-space, no code, no app change. Extends the canonical duck-typing frame with two new faces and the bind-to-a-supporting-bead move. **Frame:** [bead-duck-typing-file-folder-agent.md](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/2026-06-11/notes/bead-duck-typing-file-folder-agent.md) (file/folder/agent/module faces, the liveness spectrum, the collapse rule) · the git-as-fourth-face survey [git-and-beads.md](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/2026-06-11/notes/git-and-beads.md) · the supporting-bead runtime [bead-as-webdav-handler.md](https://redfish.acequia.io/guerin/.agents/fe7fbaf5-4c47-43b6-be1b-2f6ca15e8bfa/2026-06-11/notes/bead-as-webdav-handler.md).
## The claim in one sentence The `catalog.json` a STAC viewer fetches need not be a static deploy artifact — it can be **one face of a bead**: GET it as STAC and you walk catalog → collections → items; `git clone` it and the same URI answers as a repo (smart-HTTP); `PROPFIND` it and it answers as WebDAV; and that bead **binds to a supporting handler bead** that makes the faces *live* — generated on demand, negotiated per caller, apoptotic. This is the duck-typing claim (one URI-resource, face chosen at interaction time — not pinned) with two more faces named (**STAC** and **git**) and the agent-face supplied not by the bead itself but by a *bound* host. The static STAC/git files are projections; the supporting bead is the runtime that lights the "computing/acting" end of the liveness spectrum.
## 1. Duck-typing recap — the face is negotiated, not pinned A bead is a single **URI-addressable resource** whose type is decided by `(verb, Accept, path-suffix, caller)` at request time, not declared in advance ([bead-duck-typing-file-folder-agent](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/2026-06-11/notes/bead-duck-typing-file-folder-agent.md)). The already-ratified faces: **file** (`GET`+`Accept` → bytes in the asked representation), **folder** (`PROPFIND`/listing/`MKCOL` → a navigable container), **agent** (`OPTIONS`/intentful `PUT`/negotiated capability → a live process), **module** (`import()` / `start-bead <url>` → the handler loaded into a runtime). None is "the real one"; the resource is the superposition, the request collapses it. Discovery is built in — `OPTIONS`/`PROPFIND`/HATEOAS make the bead self-describing, no external registry ([beads.md negotiable surface](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/beads.md)). **STAC and git are two more such faces.** They differ from file/folder only in being *structured walks* over the bead's own contents, selected by suffix/Accept rather than reified into a separate service. The collapse rule still holds: static file = the degenerate agent that negotiates nothing.
## 2. STAC as a bead face A STAC `catalog.json` / `collection.json` / item is **a generated view over the bead's own contents**, not a separate dataset. The bead's subtree *is* the source; the STAC tree is its projection in the STAC shape (root catalog → collections → items, each item a Feature with `geometry`/`bbox`/`datetime` + `assets`). - **The derive-stac skill is the static instance.** [`derive-stac-from-image-cache.mjs`](https://redfish.acequia.io/guerin/.agents/65783732-7907-4a36-983a-3b015e75e80b/2026-06-13/skills/derive-stac-from-image-cache.mjs) reads a dataset's own sidecars (`imageMeta/<id>.json` + `imagePose/<id>.json`) and *emits* a STAC — structural links relative (STAC norm), **asset hrefs absolute/qualified** so they **reference the originals in place** (image = the meta `url` as-is; thumbnail = `<base>/thumbnails/<id>.jpg`). That is RTE remote-first ([log: "RTE remote-first ⇒ qualified URLs, never relative (no local co-location)"]): the STAC face is **pure metadata + pointers**, it copies no pixels. The santafe.live STAC and the Sandy/Palisades incident catalogs are exactly this — projections that bind to remote originals. - **A live bead generates the same view on GET.** The skill bakes the projection once; the next step is that `GET …/<bead>/catalog.json` is answered by the bead's handler, which walks its own `imageMeta/imagePose` (or telemetry, or whatever it holds) and renders the STAC fresh. Same output, but now demand-driven: it can reflect newly docked items, negotiate per caller (§4), and apoptose. The STAC tree stops being a `dist/` artifact and becomes the **STAC-shaped representation of a folder-face** — content negotiation ([polymorphic-get](https://redfish.acequia.io/guerin/.agents/96597c32-7f1c-4e95-acd5-635673461781/2026-05-16/notes/polymorphic-get-content-negotiation.md)) at the catalog granularity. - **The viewer is just one HATEOAS client of that face.** The incident-viewer (`index.ts`) now opens **any** STAC via `?stac=<catalog.json>`: `walkStac` fetches the root, classifies each `rel=child` by doc type (`isCollectionDoc`), and handles **both** a MULTI catalog (children = sub-catalogs = events) and a SINGLE catalog (children = collections → the root IS the event) — shape-flexible, resolving every href with `new URL(href, docUrl)`. It is a generic STAC walker that knows nothing about agency; it metabolizes the bead at its file/folder-shaped STAC face and ignores the rest. That is the duck-typing payoff: *a dumb walker and a live bead meet at the STAC face with no contract beyond STAC itself.*
## 3. git as a bead face The same URI, approached with git's smart/dumb-HTTP transport (or a `.git/` suffix), is a **clonable repo** — the **history face** ([git-and-beads](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/2026-06-11/notes/git-and-beads.md)). This is the in-bead `repo/` pattern generalized until the *bead itself* is the repo: `chats/` + `uploads/` immutable provenance ↔ commits (tamper-evident); session ticks ↔ signed commits; `/bead-sync` ↔ `git push` over the same WebDAV store; "notes drift, user words don't" ↔ `git blame`. Ratified (Stephen, 2026-06-11): *"a bead can ducktype as a git."* This bead already lives it — its app is a **git repo in the bead at `repo/`** (decision per the d9dfa5e4 git-and-beads rule: *ecology-owned source lives in-bead; vendor/coupled clones stay at `src/`; `gc` before sync*). So **one resource, three+ faces of the same bytes**: | Approach with | Face | What you get | |---|---|---| | `GET`/`Accept: application/json` + STAC walk | **STAC** | browsable catalog → collections → items, assets → originals in place | | git smart-HTTP / `.git/` | **git** | versioned + signed history; every clone is a CDN replica | | `PROPFIND` / `PUT` | **WebDAV** | writable container — dock a new image-set into a collection | | `OPTIONS` / dock-`PUT` / `import()` | **agent / module** | the live handler (supplied by the bound host, §4) | Browsability (STAC), versioning (git), and writability (WebDAV) are **not three deployments** — they are three faces of one URI. Per-face **replication** (git-and-beads' "bead CDN"): faces carry their own weight/heat — light hot `catalog.json`/notes everywhere, heavy cold `.git/` at N nodes, N demand-driven (= charge), decay-to-floor = apoptosis. Content-addressing makes every git replica self-verifying; a parciante who clones has silently joined the CDN.
## 4. Bind to a bead that can support it The static STAC/git/WebDAV faces are **projections** — they enumerate, version, serve bytes, but they do not *compute*. To negotiate per caller, generate the STAC fresh on GET, accept a dock deposit and turn it into a new item, apoptose stale entries, and participate in the advanced wave, the bead must be **bound/mounted to a supporting handler bead** — "a bead that can support it." Per [uri-bind-mount](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/uri-bind-mount.md): the namespace is *composed, not reified*; the same URI re-binds to a live runtime without moving bytes, and the composition can be **per caller** (Plan 9 style). The supporting bead is the **agent-face of a WebDAV surface** — the handler layer of declaration → routing → handler ([bead-as-webdav-handler](https://redfish.acequia.io/guerin/.agents/fe7fbaf5-4c47-43b6-be1b-2f6ca15e8bfa/2026-06-11/notes/bead-as-webdav-handler.md)): - **The handler contract** `handle(verb, path, headers, body, caller) → response` over the bead's subtree, on **two lanes**: synchronous (a live process answers in-band, `200`/`207`) and asynchronous (`PUT` into the dock → `202 Accepted` + `Location`; the answer arrives as a later `PUT`). The 202 is the **degenerate handler's honest answer** — a plain static store still accepts dock deposits, so nothing breaks when the brain is away. *Paths-as-event-bus is the RPC* ([paths-as-event-bus](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/paths-as-event-bus.md)). - **Liveness ladder** — static origin (no handler, file/folder/STAC faces + 202 dock) → service worker (per-caller negotiation in the user's browser session) → OS-process **animator** (one daemon, many beads; the bead is *config*, the animator is *runtime*). A request collapses the superposition to the most-alive runtime available and decays gracefully down the ladder. **Static file is the degenerate handler.** - **Cognition ladder** (orthogonal — *what thinks during a tick*): rung 0 deterministic `handler.mjs` (generating the STAC walk is rung-0 — pure templating over the bead's own files, **no key**) → rung 1–2 local/distilled models → rung 3 cloud frontier **BYOK** (Anthropic/Gemini), reserved for the hard tail (e.g. an *active cataloger* composing an extraction script over a half-structured source, per this bead's note 03). Most catalog faces never escalate past rung 0. - **Credentials live in nodes, not the commons.** Placement = scope × blast-radius × revocability (feedback_browser-is-a-parciante-node), uniform across runtimes; `acequia.keys` is to cognition what `acequia.chains` is to the namespace. A **web-reachable** host's cognition credential is an API key (BYOK), never a subscription seat (feedback_agentic-host-api-key-not-subscription). - **The host lends authority, never its broad key.** A SW handler runs under *the caller's* WebDAV authority (attribution falls out free); an animator writes only within the bead's own subtree + named docks — [agent-as-bead](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/agent-as-bead.md) as a runtime invariant. The bound supporting bead is the production realization: the [stephenguerin.live router-bead](https://redfish.acequia.io/guerin/.agents/82bd6fa4-4d5a-435b-9deb-ca930a82aa89/about.md)'s generic `server.js` already does `import(<bead>/handler.mjs)` to obtain `handle()` and serve it — the module-face in production. A STAC-catalog bead binds to exactly that kind of host. **Per-caller composition falls out.** The same catalog URI, bound through a supporting bead, can serve a public anon visitor only the public items and a credentialed parciante the full set — different namespace compositions of one URI, not forked deployments (§6 Q3).
## 5. Why it matters / what it dissolves - **Kills the "is it a file, a catalog, or a repo?" duplication.** You don't choose a storage form vs a service vs a VCS layout. It is one URI; bind the face you need ([uri-bind-mount](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/uri-bind-mount.md)). The "where does X live" question splits cleanly into canonical-location vs binding-set. - **STAC catalogs stop being static deploy artifacts.** Today santafe.live's `stac/` is a baked `derive-stac` output. As a live face it reflects the current contents, negotiates per caller, and versions itself (git face) — the catalog becomes a *living index of the bead*, not a frozen snapshot. - **Contribution and attribution become first-class.** A phone `PUT`ting a multi-sensor image-set into a collection's **dock** (`uploads/`) is the async-lane ingress; the handler tick turns the deposit into a new STAC item (with its pose sidecar) and the catalog face updates. Attribution flows **up the bind** — the advanced-wave ledger credits the contributor and every bead in the chain ([beads.md advanced-wave](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/beads.md); git's signed commits are the same retarded/advanced ledger from the history side). - **Lifecycle is apoptotic, not necrotic** ([apoptosis-vs-necrosis](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/apoptosis-vs-necrosis.md)). A stale item is `DELETE`d with a named cause (`410 Gone`, not `404`); a budget-exhausted catalog bead decays to its static STAC face (the fossil) and revives on the next dock deposit — correct quiescence, not failure. Charge = demand = budget: no inbound STAC walks → no ticks → no spend. - **Originals are never moved.** RTE remote-first means assets stay qualified-URL pointers; the STAC face is metadata over originals-in-place, so binding/replicating a catalog never duplicates pixels — only the lightweight index travels.
## 6. Open questions (for Stephen) 1. **Who generates the STAC face?** The bound handler **on GET** (rung-0, always-fresh, demand-priced), or a **build step** (`derive-stac` baked, cheaper/cacheable, can drift)? Likely both per-face: bake-and-cache with a live regenerate on dock-write — what invalidation signal? 2. **git-write vs WebDAV-write vs the dock — one write story or three?** A contributor can `git push`, `PUT` a member, or deposit into `uploads/`. Do all three reconcile to the same commit (the dock tick *is* a signed commit), or are git-writes the privileged "release" lane and WebDAV/dock the "draft" lane? How does the SW gate ref-writes ("who can push to prod") vs accept dock deposits from anyone? 3. **Per-caller catalog scope.** Does the STAC face expose only public items to anon, and negotiate the full set per caller capability (Plan-9 per-caller namespace), or is it one public catalog with gated *assets*? Where is the cut — at items (`links`) or at asset hrefs? 4. **Credential custody of the bound supporting bead.** When catalog-bead X binds to host-bead H, whose keys animate it — H's node keystore (`acequia.keys`, BYOK), or an attenuated chain token X mints for H scoped to X's subtree? (The import-a-foreign-bead-under-lent-authority rule: the host lends blast-radius, never its broad key.) 5. **STAC `links` rel ↔ bead bind edges.** Do STAC relations (`root`/`parent`/`child`/`item`/`self`) map onto bind/mount edges in the namespace (a `rel=child` *is* a mount of a sub-bead's collection face)? If so, a STAC walk and a namespace `PROPFIND` traverse the same graph — is `rel` the public name for a bind, and can a bead advertise non-STAC binds (`rel=animator`, `rel=replica`) in the same `links`? 6. **Replication granularity per face.** git-and-beads sets N per face (hot catalog everywhere, cold `.git/` at N nodes). At what granularity does the STAC face replicate — whole catalog, per collection, per item — and does an asset's qualified-URL origin count as its own replica class outside the bead's N?
## Related - Canonical faces + collapse rule: [bead-duck-typing-file-folder-agent.md](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/2026-06-11/notes/bead-duck-typing-file-folder-agent.md) - git as the history face / bead CDN: [git-and-beads.md](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/2026-06-11/notes/git-and-beads.md) - The supporting bead (declaration→routing→handler, lanes, ladders, BYOK): [bead-as-webdav-handler.md](https://redfish.acequia.io/guerin/.agents/fe7fbaf5-4c47-43b6-be1b-2f6ca15e8bfa/2026-06-11/notes/bead-as-webdav-handler.md) - One resource, many URIs / per-caller composition: [uri-bind-mount.md](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/uri-bind-mount.md) - Beads are agents, not bytes: [beads.md](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/beads.md) · self-model [agent-as-bead.md](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/agent-as-bead.md) - Dock/RPC substrate: [paths-as-event-bus.md](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/paths-as-event-bus.md) · lifecycle [apoptosis-vs-necrosis.md](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/apoptosis-vs-necrosis.md) - Module-face in production: [stephenguerin.live router-bead](https://redfish.acequia.io/guerin/.agents/82bd6fa4-4d5a-435b-9deb-ca930a82aa89/about.md) - Concrete STAC work this bead: the `?stac=` viewer (`Documents/src/incident-viewer/index.ts` — `walkStac`/`STAC_ROOT`/`isCollectionDoc`) + the generator [derive-stac-from-image-cache.mjs](https://redfish.acequia.io/guerin/.agents/65783732-7907-4a36-983a-3b015e75e80b/2026-06-13/skills/derive-stac-from-image-cache.mjs)
## References (bead cross-links) - Bead: Agentify Bead · [canonical](https://redfish.acequia.io/guerin/.agents/d9dfa5e4-3c92-4216-bd53-13b520c59a5e/) - Bead: As Webdav Handler · [canonical](https://redfish.acequia.io/guerin/.agents/fe7fbaf5-4c47-43b6-be1b-2f6ca15e8bfa/) - Bead: 874fce5b · [canonical](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/) - Bead: 96597c32 · [canonical](https://redfish.acequia.io/guerin/.agents/96597c32-7f1c-4e95-acd5-635673461781/) - Bead: Stephenguerin.live Router Bead · [canonical](https://redfish.acequia.io/guerin/.agents/82bd6fa4-4d5a-435b-9deb-ca930a82aa89/)