**Note** from Bead: 31bd5380 · [canonical source](https://redfish.acequia.io/guerin/.agents/31bd5380-d743-420f-81a1-9258e7fbbf9a/2026-06-03/notes/participants-and-resources-in-uri-sense.md) · session 2026-06-03 · discussion: Talk: 31bd5380
A worked expression of the user-as-URI inversion the fused critique flagged as a structural gap. Vocabulary shift first; then the URI-side view of how participants manage layers, models, scenarios, sessions, and general resources.
## Vocabulary shift **`user` → `participant`.** Flatly. Everywhere. The replacement is not just word-swapping; it carries weight: - **Anchor 1 — parciante** (NM acequia tradition): the water-rights holder. The parciante has a *stake* in the acequia (a real claim to flow), is *identifiable*, *attends meetings, votes, contributes labor* (the annual `saca` cleaning). The parciante is the FUNDAMENTAL unit of agency. Sub-roles (mayordomo as ditch-boss, comisión as governance body) are *roles* the parciante can hold — not separate classes of being. - **Anchor 2 — Zoom participant**: someone in a meeting. May *hold a role* (host, co-host, attendee). May join from multiple devices. Identifiable by name. Can do different things based on the role they hold *in that session*. Capabilities are session-scoped; identity persists across sessions. Both anchors say the same structural thing: **the entity exists across many contexts; the entity's role in any given context is a binding, not a class membership.** This dissolves a pattern in the current pitch where "user," "owner," "admin," "editor," "viewer," "host," "participant," "observer" all appear as if they're parallel kinds-of-thing. They're not. There's one **kind-of-thing** (the participant) and many **role-bindings** the participant can hold. ### Host is a role of a participant **`host` is not a kind of participant.** It is a role a participant *holds* for a specific scope, with raised rights and responsibilities for that scope: | Scope | What host means | How long the role lasts | |---|---|---| | **Session** (e.g., a Simtable session) | The participant who claimed the table; can configure session-level settings, invite participants, transfer host, end session | The session's duration; transferable mid-session | | **Group** | The participant(s) responsible for managing the group's membership and resources | Until they leave or transfer | | **Organization** | Renamed from "owner/admin"; the participant(s) responsible for org-level governance | Until succession | | **Resource** (a scenario, a dataset) | The participant who controls the resource's metadata, sharing, and lifecycle | Until ownership-transfer or apoptosis | A single participant typically holds many host-roles at any moment (host of their own personal namespace; host of a session they claimed; host of a group they founded; etc.) and many non-host roles (editor in some orgs, viewer in others, participant in some sessions). ### Other vocabulary the shift touches - **`observer`** stays. An observer is *present but not authenticated* — they have no URI in the session and don't bind to anything. They watch. They are explicitly *not participants*. - **`organization`**, **`person`**, **`ai-agent`** are all **subclasses of participant** — see "Three subclasses of participant" below. (Earlier drafts of this note said "organization is not a participant"; that was wrong. All three are participants, sharing the same protocol, differing in what they typically hold.) - **`mayordomo`** is the *agent at a participant's URI* that mediates policy. For an organization, the mayordomo is the autonomous policy hand acting on behalf of the org's governance. For a person, the mayordomo may be a personal AI assistant (if delegated) or absent. For an AI-agent participant, the agent **is its own mayordomo** at its URI — no separation. A human participant may *direct* a mayordomo; the mayordomo is the agent at the URI, not the human at it. - **`device`** is a thing the participant uses; it has its own URI and key, but it's not a participant itself. (A laptop doesn't *vote at the meeting* — the participant using the laptop votes.) - **`session`** is a resource (a URI) with its own lifecycle. Participants bind to sessions; sessions don't *belong to* participants any more than meetings belong to attendees. ### Words to retire from the spec's surface vocabulary The current spec's vocabulary table (effort-1 §2) implies these are first-class concepts: - `User` → participant (and specifically, the *person* subclass; orgs and agents are also participants — see next section) - `Owner` / `Admin` / `Editor` / `Viewer` / `Participant` (as separate roles in a list) → all become *role-bindings*, not types. Keep the labels as binding-template names ("the editor binding"), but stop treating them as parallel-kinds-of-user. - `Membership` → a *binding* from a participant URI to an org or group URI with a role-pattern - `Capability map` → a *view* of the bindings a participant URI currently holds, with their attenuations and TTLs
## Three persistent identities **Correction note** (2026-06-03): an earlier draft of this section was titled "Three subclasses of participant" and treated person/org/agent as participant subclasses. Stephen's Zoom-anchor correction clarified that **participant is a session-relationship, not an identity class**. Person, organization, and AI agent are the three **persistent identities** in the system; participant is a binding *between* an identity and a session. The fully revised vocabulary lives in [user-representation-vocabulary.md](https://redfish.acequia.io/guerin/.agents/31bd5380-d743-420f-81a1-9258e7fbbf9a/2026-06-03/notes/user-representation-vocabulary.md). The three persistent identities, all sharing the same vocabulary and protocol (URI, bindings, capability map, audit, role-on-binding, apoptotic lifecycle) but differing in what they typically hold and need: ### Organizations - **Collectives with subscription accounts** — the billing relationship that funds the platform lives here. - Hold member-bindings (people, sometimes agents) and own resources at their URI subtree. - Act through their **hosts** (human governors) and their **mayordomo** (the autonomous policy-mediating agent at the org's URI). - Examples: a fire department, a research lab, a city, a community group, a federation of organizations. - URI convention: typically under `/orgs/<slug>` — naming convention, not semantic distinction. The org URI is a participant URI. ### People - **Individual humans.** - Cryptographic identity backed by **device keys** (PS256 keypairs in the current Acequia substrate, one per registered device). - May hold member-bindings to organizations; may host sessions; may own personal resources at their URI subtree; may have multiple devices bound to one person URI. - Examples: a fire chief, a PIO, a training coordinator, a community member. - URI convention: typically under `/people/<handle>` or `/participants/<handle>`. ### AI agents - **Autonomous AI entities at their own URIs.** - Cryptographic identity backed by **process keys** (long-running service identity, not per-device). - May be a *mayordomo* serving an organization or session; may be a domain-specific assistant; may be a long-running review/research bead. - Examples: an org's policy-mediating mayordomo; a wildfire-modeling agent participants can query; a triage agent that mediates incident-response workflows; the bead writing this note. - URI convention: typically under `/agents/<name>` or under a host org's `/agents/` subtree. - **The bead system already is the participant protocol applied to AI agents.** Every bead at `redfish.acequia.io/guerin/.agents/<GUID>/` is an agent-participant URI; `about.md`, `notes/`, `chats/`, `artifacts/`, `uploads/` are sub-URIs. The blackboard protocol works precisely *because* the receiving bead is a participant whose mayordomo (= the agent at the URI) processes incoming bindings. ### What they share (the participant protocol) All three subclasses: - Have a URI in the namespace - Hold bindings (memberships, role-bindings, ownership, grants received/given) - Have a computed capability map (`GET <participant-URI>` with bindings expanded) - Have an audit log at `<participant-URI>/audit/` - Can hold the host role for a scope (session, group, organization, resource) with raised rights/responsibilities - Are subject to apoptotic lifecycle (orderly succession, key rotation, signaled termination) ### What they differ in | | Organizations | People | AI agents | |---|---|---|---| | **Billing / subscription** | Typically yes (the account that funds the platform) | Usually no (personal accounts possible) | Usually billed via host org | | **Identity backing** | Corporate signing key, usually delegated to a host or mayordomo | Device keys (one per registered device) | Process keys (long-running service identity) | | **Direct agency** | Acts through hosts + mayordomo | Acts directly via authenticated device | Acts directly within its scope | | **Member-bindings** | Typically *has* members | Typically *is a member* of orgs; rarely has members | Typically scoped to a host org; rarely has members | | **Apoptotic signal** | Succession model (N-of-M hosts) handles continuity | Device revocation, account closure | Process termination; replaced by next-version agent | | **Sessions** | Owns simtables and other session-hosting devices | Hosts and participates in sessions | Joins sessions to provide capabilities (modeling, triage, translation) | ### The mayordomo / participant distinction (recap) The **mayordomo is the agent at a participant's URI**. The relationship by subclass: - **Organization** ↔ mayordomo separation: the org IS the collective; the mayordomo IS the autonomous agent that acts on the org's behalf at the org URI. Distinct entities, related by binding (the mayordomo is the org's policy hand). - **Person** ↔ mayordomo separation: optional. A person can delegate a personal AI assistant to handle routine matters at their URI; many won't. Most person URIs have no mayordomo and the person responds directly through device authentication. - **AI agent** ↔ mayordomo separation: **none**. The agent IS its own mayordomo at its URI. There's no separation between "the agent" and "the agent's coordinator" because the agent is the coordinator. This keeps the [agent-as-bead](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/agent-as-bead.md) frame intact: every URI can host an agent; for AI-agent participants, the agent IS the bead.
## URIs everywhere The frame: **every addressable thing in the system has a URI.** Participants, devices, sessions, organizations, groups, resources (layers, scenarios, progressions, datasets), and the policy surfaces of all of the above. Operations are HTTP verbs on those URIs (GET, PUT, POST, PROPFIND, MKCOL, COPY, MOVE, DELETE, LOCK, PROPPATCH). Cross-references are *bindings* — URI → URI relationships with attenuation/TTL/scope metadata. ### Concrete URI patterns > Examples are illustrative — actual paths are Kaz's call. The point is the *shape*. **People participants** (one URI per person; cross-subdomain identity opt-in via the Acequia Passport): ``` https://anyhazard.acequia.io/people/jane-smith https://realtime.earth/people/jane-smith # different namespace, same person https://acequia.io/passport/jane-smith # the optional cross-subdomain binding ``` **Devices** (the person's hardware; each has its own keypair; bound under the person's URI): ``` https://anyhazard.acequia.io/people/jane-smith/devices/laptop-2024 https://anyhazard.acequia.io/people/jane-smith/devices/phone https://anyhazard.acequia.io/people/jane-smith/devices/simtable-sfd-1 ``` **Organization participants** (collectives with subscription accounts and mayordomos at their URIs): ``` https://anyhazard.acequia.io/orgs/sfd # San Francisco Fire Department https://anyhazard.acequia.io/orgs/sfd/members # membership listing https://anyhazard.acequia.io/orgs/sfd/policy # org-wide settings https://anyhazard.acequia.io/orgs/sfd/billing # subscription / seat counts / quota https://anyhazard.acequia.io/orgs/sfd/mayordomo # the org's autonomous policy agent https://anyhazard.acequia.io/orgs/sfd/audit # audit log ``` **AI-agent participants** (autonomous agents at their own URIs; may live under an org or stand alone): ``` https://anyhazard.acequia.io/agents/wildfire-modeler-v2 # standalone domain-specific agent https://anyhazard.acequia.io/orgs/sfd/agents/incident-triage # org-scoped triage agent https://redfish.acequia.io/guerin/.agents/31bd5380-d743-420f-81a1-9258e7fbbf9a/ # the bead writing this note ``` **Note** (added 2026-06-03 after reading canonical platform docs): an earlier draft of this note had "Application identities" with URI patterns like `/apps/<name>`. **Removed** — the canonical platform doesn't model applications as identities; apps are resources owned by an identity and authenticated via **named API tokens** issued by their owner. The naming convention `<scope-or-org>-<role-or-purpose>` (e.g., `wiki.acequia.org-mayordomo`) is how apps and other non-human-acting roles present identity at the token level. See [user-representation-vocabulary.md](https://redfish.acequia.io/guerin/.agents/31bd5380-d743-420f-81a1-9258e7fbbf9a/2026-06-03/notes/user-representation-vocabulary.md) → Tokens → "Naming convention for non-human-identity API tokens." **Groups** (lighter than orgs; can be sub-bound under an org or stand alone): ``` https://anyhazard.acequia.io/groups/wildfire-2026-spring https://anyhazard.acequia.io/orgs/sfd/groups/incident-response-alpha ``` **Resources** (layers, scenarios, progressions, models, datasets — all uniform under "resource"): ``` https://anyhazard.acequia.io/orgs/sfd/scenarios/2026-spring-drill https://anyhazard.acequia.io/orgs/sfd/layers/fuels/sf-county-2025 https://anyhazard.acequia.io/orgs/sfd/progressions/2024-incident-alpha-replay https://anyhazard.acequia.io/participants/jane-smith/scenarios/personal-2026-04 https://realtime.earth/datasets/usgs/elevation/v2024-q1 ``` **Sessions** (bounded periods of multi-participant interaction at a device): ``` https://anyhazard.acequia.io/sessions/2026-06-03T10-sfd-table-1 https://anyhazard.acequia.io/orgs/sfd/sessions/2026-06-03T10-sfd-table-1 # also bound under owning org ``` **Policy surfaces** (where mayordomos read/write): ``` https://anyhazard.acequia.io/orgs/sfd/queue # pending approval requests https://anyhazard.acequia.io/orgs/sfd/policy/mode # operating mode of org-owned tables https://anyhazard.acequia.io/participants/jane-smith/capabilities # computed capability map https://anyhazard.acequia.io/participants/jane-smith/inbox # incoming shares, requests, notifications ``` The point is **uniformity**: there are no "user records" vs "resource records" vs "session objects" vs "API endpoints." Everything is at a URI; everything responds to a small uniform verb set; everything is composable via binding.
## How participants manage resources — the operations Every operation is a verb on a URI. Here are the operations Kaz's pitch enumerates (across all three efforts) recast as URI-level operations. | Pitch operation | URI-side expression | |---|---| | **Create a resource** | `PUT https://.../<owner>/resources/<name>` (owner = the participant URI or org URI under which the resource lives) | | **Browse my catalog** | `GET https://.../participants/jane/` (returns the participant's bound view: own resources + memberships' resources + shared-in + session-imports) — *per-caller composition*; what you see depends on who you are | | **Share resource R to participant P** | `PUT https://.../participants/P/inbox/<binding-name>` with body referencing `R`'s URI and the chosen scope/TTL. Reciprocal entry created at `https://.../resources/R/shared-with/P` | | **Share resource R to org X (federation)** | Same — bind `R`'s URI under `https://.../orgs/X/incoming/` | | **Lock resource R** | `LOCK https://.../resources/R` (standard WebDAV) — locks the metadata against modification; force-unlock by higher-role mayordomo with audit trail | | **Request access to R** | `POST https://.../resources/R/requests/` — lands in `R`'s owner's queue (which is itself a URI: `https://.../<owner>/queue/`) | | **Approve a request** | `PUT https://.../<owner>/queue/<req-id>/approval` — triggers the corresponding `inbox` write to the requester | | **Revoke a share** | `DELETE https://.../participants/P/inbox/<binding-name>` — cascade revocation follows the chain-token graph | | **Move R between devices** | No operation. The resource has *one URI*; the device's local cache resolves it. The "move" is a cache hint, not a data transfer. (Today: a sync operation. Tomorrow: a service-worker routing decision.) | | **Attach R to session S** | `PUT https://.../sessions/S/imports/<name>` with body referencing `R`'s URI and a session-scoped attenuation. Auto-revokes on session end. | | **Join session S as participant** | `PUT https://.../sessions/S/participants/<participant-URI>` — the session URI records the binding; the participant URI gets a reciprocal `<participant-URI>/active-sessions/<S>` | | **Claim session as host** | Same as join, but with `host` role-binding. Subject to the session's `policy/host-eligibility` check. Open to any person/agent identity (not restricted to users-of-org); subject to free-tier capacity limits unless backed by personal-premium or org-seat capacity. | | **Transfer host** | `MOVE https://.../sessions/S/host` — atomic; old host's host-role-binding gone, new host's host-role-binding established, audit-logged | | **End session** | `DELETE https://.../sessions/S` (or `PROPPATCH` to mark `state: ended`) — apoptotic signal propagates: all session-scoped bindings revoke, audit log finalizes, session URI persists as historical record | | **Promote DIY table to org-owned device** | `MOVE https://.../participants/jane/devices/diy-table-3` → `https://.../orgs/sfd/devices/diy-table-3` — atomic transfer; historical bindings preserved | | **Federate to peer org** | `PUT https://.../orgs/peer-org/incoming/<binding>` with body referencing the resource URI and the federation scope. Peer's mayordomo can `DELETE` to revoke. | Notice what falls out: - **The nine separate sharing/federation/grant/move features collapse to one parameterized operation**: bind a resource URI into a destination URI's namespace, with scope and TTL. The destination determines the semantics (`inbox` = share-to-participant; `incoming` = federation; `imports` = session-attach; `subscribers` = pub/sub). - **The cross-source dedup problem disappears**: if the same resource is reachable through multiple bindings, the participant's view shows one resource with N binding-trails. No special case in code. - **Lifecycle uniformly via DELETE / apoptotic signal**: end session, revoke share, archive group, key rotation — all the same shape (DELETE the binding, propagate the signal, cascade through chain-token graph).
## What the participant sees — the capability map in URI terms
The participant asks: "what can I do right now?" The answer is the union of their bindings, presented as a navigable view.
`GET https://anyhazard.acequia.io/participants/jane-smith/capabilities` returns something like:
```yaml
participant: https://anyhazard.acequia.io/participants/jane-smith
passport: https://acequia.io/passport/jane-smith # optional cross-subdomain unifier
devices:
- https://.../participants/jane-smith/devices/laptop-2024
- https://.../participants/jane-smith/devices/phone
- https://.../participants/jane-smith/devices/simtable-sfd-1
memberships:
- org: https://.../orgs/sfd
role: editor
granted-paths: /orgs/sfd/layers/fuels/
granted-via: org-policy-template "editor-default"
- group: https://.../groups/wildfire-2026-spring
role: host
granted-paths: [all]
granted-via: founder
inbox:
- resource: https://.../orgs/peer-fd/scenarios/2026-jan-incident
permission: read
expires: 2026-07-01
granted-by: peer-fd-admin
granted-at: 2026-06-15
active-sessions:
- session: https://.../sessions/2026-06-03T10-sfd-table-1
role: host
started: 2026-06-03T10:15Z
table: https://.../orgs/sfd/devices/simtable-sfd-1
session-scoped-grants: [...]
own-resources:
- https://.../participants/jane-smith/scenarios/personal-2026-04
- https://.../participants/jane-smith/notes/training-prep
pending:
- 2 approval requests at https://.../orgs/sfd/queue waiting on you (host of org policy)
- 1 inbound federation request from org peer-fd
```
The capability map *is* the participant's URI rendered with their bindings expanded. It's not a separate computed thing — it's just `GET <participant-URI>` with depth and per-caller composition.
The pitch's F2.6 ("Capability map") becomes the *natural* way to interact with the system, not a separate feature that has to be built. The current feature framing ("the capability map should explain *why* a permission exists") is exactly what a binding-trail gives you for free — every entry above carries `granted-via` provenance.
## Per-caller composition (Plan-9 style) The same URI returns different namespaces to different callers. `GET https://anyhazard.acequia.io/orgs/sfd/` returns: - *To a non-member*: org name, logo, public scenarios. Nothing else. - *To an editor*: the above, plus full `/orgs/sfd/scenarios/`, `/orgs/sfd/layers/fuels/`, `/orgs/sfd/groups/` they belong to. - *To a host (admin-equivalent)*: the above, plus `/orgs/sfd/queue/`, `/orgs/sfd/policy/`, `/orgs/sfd/audit/`, `/orgs/sfd/members/`. - *To the org's mayordomo*: the above, plus internal coordination state. - *To a participant from a federated peer org*: the federated subtree only. This is the same operation (GET on the same URI), composed differently at the moment of request. The pitch's notion of "different things are visible to different people" stops being a per-feature access-control rule and becomes a property of *how the namespace renders*. Implementation note (in Document B): each participant's request is evaluated against their capability map, and the response is the intersection of "what exists at this URI" and "what bindings this participant holds for this URI's subtree."
## Dynamic / live resources Not every URI resolves to a stored file. The [agent-as-file-ducktyping](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/agent-as-bead.md) frame applied here: a URI can resolve to a camera, a sensor, a simulation in progress, a running process, or a stream — not just a file. Example dynamic URI patterns: ``` https://anyhazard.acequia.io/orgs/sfd/cameras/watchtower-3 # camera (GET returns latest frame) https://anyhazard.acequia.io/orgs/sfd/cameras/watchtower-3/stream # SSE/WebSocket subscription for live frames https://anyhazard.acequia.io/orgs/sfd/simulations/2026-06-03-evacuation-drill # simulation (GET returns current state) https://anyhazard.acequia.io/orgs/sfd/simulations/.../history # replay sub-URI (returns past states) https://realtime.earth/sensors/weather/station-bozeman-1 # sensor (GET returns latest reading) https://anyhazard.acequia.io/orgs/sfd/processes/incident-response-active # running process ``` ### Worked example: a Simtable camera A camera on an AnyHazard Simtable is a *tree* of related URIs, not a single URI. The full pattern (carried in detail in [user-representation-vocabulary.md](https://redfish.acequia.io/guerin/.agents/31bd5380-d743-420f-81a1-9258e7fbbf9a/2026-06-03/notes/user-representation-vocabulary.md#worked-example-simtable-cameras)): ``` /orgs/sfd/devices/simtable-1/cameras/overhead/ ├── latest # GET → latest frame (dynamic content) ├── stream # GET (SSE/WebSocket) → live subscription ├── params/ │ ├── focus-distance # GET/PUT │ ├── shutter-speed # GET/PUT │ ├── gain # GET/PUT (ISO/gain) │ └── resolution # GET/PUT ├── actions/ │ ├── capture # POST │ ├── calibrate # POST │ └── focus-here # POST ├── capabilities # GET → supportedCapabilities (HATEOAS-style discovery) └── audit # GET → who changed what when ``` Per-role permissions: observers get nothing; participants read latest/stream/capabilities and usually params/*; operators get params/* write + actions/* POST; owners full control. Per-caller composition: each caller's GET on `cameras/overhead/` returns the sub-tree their bindings authorize. This is the canonical example of: - A URI tree as a resource (vs a single URI) - The agent-as-file-ducktyping frame at the user-vocab level - Per-sub-URI permissions - Per-caller composition for discovery - HATEOAS capability negotiation Operations on dynamic URIs: | Intent | URI verb + path | |---|---| | Read latest frame from a camera | `GET <camera-URI>` | | Subscribe to camera stream | `GET <camera-URI>/stream` with `Accept: text/event-stream` | | Start a simulation | `POST /orgs/<O>/simulations/` with parameters; returns simulation URI | | Read current simulation state | `GET <sim-URI>` | | Attach a live camera to a session | `PUT /sessions/<S>/imports/<name>` referencing the camera URI | | Stop a simulation (apoptotic) | `DELETE <sim-URI>` or `PROPPATCH <sim-URI>` state=stopped | | Subscribe to incident updates | `GET <process-URI>/stream` | URI hosting splits for dynamic URIs: - **Source hosting** — the peer that runs the computation, connects to the sensor, owns the camera. Usually singular. - **Cache hosting** — serves the latest known value with staleness metadata. Horizontally scalable. - **Stream hosting** — relays subscription updates. Tree-shaped. Heavy participants of a popular camera or simulation may take on cache-hosting or stream-hosting (CDN-participation extended to dynamic URIs). The source can stay singular (the actual camera) while caches and stream relays distribute the read load.
## Resource ownership and permissions Every resource has an **owner** (one of the three identity types — person, organization, or AI agent). The owner has full control: read, write, share, transfer ownership, delete, change permissions, designate URI hosts. Permissions on a resource are granted via bindings: - `read` — view content - `write` — modify content (typically implies read) - `share` — re-grant access to other identities, attenuated by the "don't grant more than you have" rule Bindings combine these (a typical editor binding = read + write; a co-owner binding = read + write + share). The binding lives at the recipient's URI (`<recipient>/inbox/<binding>` or under a session/group as appropriate) and references the resource URI. Ownership transfer: `MOVE /resources/<R>/owner` to a new identity URI; subject to acceptance by the new owner.
## URI hosting (a distinct sense of "host") Every URI exists at some **host(s)** — peers or servers that serve its content. This is the *third* sense of "host" in the vocabulary (alongside session-host and mayordomo-as-governance-role). **Plural by design**: a URI is hosted at multiple peers forming a distributed availability mesh. Operations at the URI-hosting level: | Intent | URI verb + path | |---|---| | Pin a URI (commit to host it locally) | `PUT /people/<P>/pinned/<URI-slug>` | | Unpin (release commitment) | `DELETE /people/<P>/pinned/<URI-slug>` | | List hosts for a URI | `GET <R-URI>/hosts/` | | Owner deprecates a URI (apoptotic signal) | `PROPPATCH <R-URI>` state=deprecated; hosts choose to keep serving or release | | Reaffirm hosting (extend SLA) | `PROPPATCH /people/<P>/pinned/<URI-slug>` ttl=+K-days | **URI participant** generalizes session-participant: any identity that interacts with a URI (reads, writes, references, attends sessions at it). Heavy URI participants are natural candidates to become URI hosts — either by opt-in pinning or by policy threshold (CDN participation). **The acequia/saca anchor lands directly here.** Parciantes contribute labor (saca / annual ditch-cleaning) to maintain the shared acequia that carries water for all. Same pattern at the URI level: URIs carry content for all participants; participants carry the URIs. The infrastructure-and-its-maintainers are one community, not two. **The Hubler frame anchors the dynamics:** heavy demand on a URI causes hosting to converge — peers that frequently access a URI are the natural candidates to host it. The CDN-participation policy operationalizes this dynamic. Refusal-to-host-when-you-depend-on-it is the digital analogue of being a free-rider on the saca.
## Sessions, hosts, and the Simtable In URI terms, a Simtable session is a *session URI* into which participants bind themselves (as host or non-host participants), and into which resources are bound for the session's duration. ``` GET https://anyhazard.acequia.io/sessions/2026-06-03T10-sfd-table-1/ → returns: state: active started: 2026-06-03T10:15Z table: https://.../orgs/sfd/devices/simtable-sfd-1 owning-org: https://.../orgs/sfd mode: organization # vs guest host: https://.../participants/jane-smith participants: - https://.../participants/mike-jones - https://.../participants/peer-fd/sarah-kim imports: - bound-as: scenario-spring-drill from: https://.../orgs/sfd/scenarios/2026-spring-drill bound-by: jane-smith scope: read+annotate ttl: session-end - bound-as: live-incident-layer from: https://.../orgs/peer-fd/layers/active-incidents/2026-06-03 bound-by: sarah-kim scope: read ttl: session-end session-log: https://.../sessions/2026-06-03T10-sfd-table-1/audit ``` The pitch's effort-1.1 "host / participant / observer" vocabulary maps cleanly: host is a session-scoped role-binding; participant is a session-scoped role-binding; observer is *no binding* (they're just physically present, watching what's rendered). Operations on the session: - **Claim** = `PUT /sessions/<S>/host` with the claimant's participant URI + the session's host-eligibility check (via mode, allow-list, approval queue) - **Join** = `PUT /sessions/<S>/participants/<P>` after auth (QR/short-code from the table generates a short-lived bind-token) - **Bring-your-own resource** = `PUT /sessions/<S>/imports/<name>` (subject to your own capability on the source resource) - **Host transfer** = `MOVE /sessions/<S>/host` (atomic; old binding gone, new binding present, audit-logged) - **End session** = `PROPPATCH /sessions/<S>` setting state to `ended`; apoptotic signal propagates, all `imports/*` bindings revoke, all `participants/*` bindings revoke, audit finalizes - **Idle/unclaimed** = the session URI doesn't exist yet (or exists in a `state: ended` historical record); the table URI shows the owning org's branding and a "tap to claim" affordance The current spec's "session-scoped grant" becomes "binding with `ttl: session-end`." One mechanism, many uses.
## Comparison: current spec vocabulary ↔ URI-frame vocabulary | Current spec | URI frame | |---|---| | `userId` | participant URI | | `deviceId` | device URI (sub-URI of participant) | | `instanceId` | session URI (or sub-URI of session for the participant's tab/instance) | | User record | the participant URI itself, with its bindings expanded | | Organization (Acequia subdomain owned by org-owner user) | organization URI (a resource); participants hold membership-bindings to it | | Group | group URI; participants hold membership-bindings | | Membership | a binding from participant URI → org/group URI with role-pattern | | Role (Owner/Admin/Editor/Viewer/Participant/Pending) | binding-template names; the binding itself carries the role | | Role template | a named pattern of bindings applied as a unit | | Capability map | `GET <participant-URI>` with bindings expanded; per-caller composition makes it tractable | | Invitation | a pending binding-template at `<inviter-URI>/invitations/<id>` awaiting an `accept` PUT from the recipient | | Device-link offer | a short-lived bind-token redeemable to add a device URI under an existing participant URI | | Chain token (Acequia) | the cryptographic backing for a binding; implementation detail in Document B | | Stored token | a long-lived bind-token issued to a non-human caller (script, automation); revocable via DELETE on the binding | | "Share a resource" | bind resource URI into recipient URI's `inbox/` | | "Lock a resource" | `LOCK` on the resource URI | | "Federation" | bind resource URI into peer-org URI's `incoming/` | | "Bring-your-own to session" | bind resource URI into session URI's `imports/` with `ttl: session-end` | | "Move between devices" | the resource has one URI; the device's local cache resolves it; no move operation, just routing | | "Host of a session" | binding from participant URI → session URI with `host` role | | "Approval queue" | `<owner-URI>/queue/` — a URI listing pending requests for that owner | | "Audit log" | `<scope-URI>/audit/` — append-only sub-URI under every scope (participant, session, org, resource) | | "Bring-your-own to session" auto-revoke on leave | apoptotic signal from the `participants/<P>` binding being DELETE'd cascades to all `imports/` bound by `P` | | "Resource Pointer" (effort-2) | the binding itself; one resource URI, many bindings pointing at it | Several current-spec features simply *disappear* under this frame: - **Cross-source dedup hint** (effort-2 F1.8): the participant's view naturally renders one resource entry with multiple binding-trails. The "hint" becomes the default representation. - **Pickup queue** (effort-2 F6.3): a resource has one URI; opening the app on the receiving device shows the resource immediately. The "pickup" is just URI resolution. - **Stored tokens** (effort-1 F2.4): subsumed into "bindings issued to non-human callers." The user-facing concept becomes "authorize a script to act on my behalf" — the implementation is "issue a binding-token at `<participant-URI>/tokens/<id>`."
## What this gives Kaz The pitch can be rewritten in this vocabulary *without changing the substance*. Every feature in efforts 1, 1.1, and 2 has a URI-side expression. The advantages: - **The vocabulary is self-explanatory at the operational level.** "Bind R into S's imports with TTL session-end" describes both the user intent and the implementation, in the same words. - **The role-templates become composable, not enumerative.** Don't list "Owner / Admin / Editor / Viewer" as types — define the binding patterns, and "Editor" is the name of one pattern. New roles are new patterns, not new code. - **The host concept generalizes cleanly.** Session host, group host, org host, resource host — all the same shape (a binding with raised rights for a scope). No special-case "owner" type. - **The capability map is no longer a separate feature.** It's just `GET <participant-URI>`. Document A doesn't need a section explaining capability maps; the namespace is the capability map. - **Cross-app reuse falls out.** The capability vocabulary Gemini called for (Editor in AnyHazard ↔ Editor in alert.live without redundant definitions) is just "the editor binding-pattern applied at different URIs." A participant's `editor` role at `https://anyhazard.acequia.io/orgs/sfd/` means write bindings to specific subtrees; the same template applied at `https://alert.live/orgs/sfd/` does the analogous thing in that namespace.
## What this asks of Kaz This vocabulary shift is a Document-A-level commitment (vocabulary), not a Document-B-level decision (implementation). The implementation may still ride on Acequia chain-tokens, PS256 keypairs, WebDAV — those are the *mechanism* by which the bindings are cryptographically secured. The vocabulary commitment is what the spec *says* to its readers. Three pieces of work to make this shift real: 1. **Rewrite effort-1 §2 (Core concepts) in participant/binding vocabulary.** Replace "User" with "Participant"; replace "Role" with "Role-binding"; introduce "Binding" and "Mount" as first-class concepts; recast organization, group, session, resource as URIs. 2. **Rewrite the feature areas in operation-on-URI form.** Each F-number gets an "URI-side" sub-section that names the HTTP verb and the path. Implementation backing (chain token, WebDAV, PS256) stays in Document B. 3. **Adopt "host" as a role-binding name across the spec.** Today the spec has "Owner" / "Admin" / "Session host" / "Session participant" mixed with role-templates. Unify: any participant in any scope can be a host of that scope; the rest are non-host participants with whatever bindings they hold. The shift doesn't break the current pitch — every feature still exists, every primitive still rides on Acequia. The shift makes the *vocabulary* match the *structure* that's already there.
## References (bead cross-links) - Bead: 874fce5b · [canonical](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/)