oficina / meet Public

docs: reflow PRODUCT.md to ~72 columns (W6-15)

PRODUCT.md wrapped its opening phrase short (~65) and its
continuation lines long (~100), unlike README.md, AGENTS.md and the
spec, which all wrap at roughly 70-72. Reflow only; no wording
changed (verified word-for-word against the prior commit, ignoring
blockquote markers and line breaks).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzvnuKhLzudvq7jZ7SvVt2
Paul Campbell pushed by paul@keymail.dev c22cae73f6dcd98455c97c8ac2f3ccf1030d926c parent bf3b86b
1 file changed, +171 −134
  • PRODUCT.md +171 −134

CI passed — run details

diff --git a/PRODUCT.md b/PRODUCT.md
index 791ce01..10dfc17 100644
--- a/PRODUCT.md
+++ b/PRODUCT.md
@@ -2,213 +2,250 @@
<!-- impeccable:product-schema 1 -->
-> **How this file was written.** Written unattended, on the same instruction that produced
-> `oficina/docs`' `PRODUCT.md`: every fact below comes from repository evidence, not an
-> interview — `docs/superpowers/specs/2026-09-07-meet-design.md` (revision 9, plus the
-> 2026-09-10 and 2026-09-12 amendments), `AGENTS.md`, the slice records under
-> `docs/superpowers/reviews/`, `native/README.md`, and the shipped code in `internal/meet/`
-> on branch `slice/6-deploy`, main `9225ec2`. Facts are cited to a spec section or a file. A
-> reading rather than a citation is marked **[inferred — confirm]** and should be corrected,
-> not trusted. A bare "ruling N" is the Meet spec's §1; a doctrine ruling is always written
-> "suite ruling N".
+> **How this file was written.** Written unattended, on the same
+> instruction that produced `oficina/docs`' `PRODUCT.md`: every fact
+> below comes from repository evidence, not an interview —
+> `docs/superpowers/specs/2026-09-07-meet-design.md` (revision 9, plus
+> the 2026-09-10 and 2026-09-12 amendments), `AGENTS.md`, the slice
+> records under `docs/superpowers/reviews/`, `native/README.md`, and the
+> shipped code in `internal/meet/` on branch `slice/6-deploy`, main
+> `9225ec2`. Facts are cited to a spec section or a file. A reading
+> rather than a citation is marked **[inferred — confirm]** and should
+> be corrected, not trusted. A bare "ruling N" is the Meet spec's §1; a
+> doctrine ruling is always written "suite ruling N".
## Platform
-Web, plus native Mac and iPhone/iPad clients over the same server contracts (spec §8.4,
-`native/README.md`).
+Web, plus native Mac and iPhone/iPad clients over the same server
+contracts (spec §8.4, `native/README.md`).
## Users
-**Primary: a member of one team**, making and joining that team's video calls. The instance
-boundary is the team — one instance per team (preamble; suite ruling 1, 5, §0.2), the
-"lone shape" until a suite home exists (§7.1). **Secondary: an outside guest**, admitted with
-a real identity, never as an anonymous URL holder: every participant signs in, and a meeting
-link gives an outsider a **guest grant**, never a way in without a name (ruling 1; suite ruling
-15); a guest
-waits until an admitted member lets them in (ruling 9, §3.3).
-
-Access is a magic link gated by `MEET_ALLOWED_ADDRESSES` (`internal/meet/config.go`), with
-optional Home sign-in when an operator sets all four `MEET_HOME_*` settings (§7.1.1) — Home
-only *links* an existing member after independent local sign-in; it never creates a person or
-grants membership. Unlike Docs' §8.1, the spec states no passkey ruling of its own. **[inferred
-— confirm]** the team is assumed small enough that an address allowlist is appropriate;
-the spec states no target size.
+**Primary: a member of one team**, making and joining that team's video
+calls. The instance boundary is the team — one instance per team
+(preamble; suite ruling 1, 5, §0.2), the "lone shape" until a suite home
+exists (§7.1). **Secondary: an outside guest**, admitted with a real
+identity, never as an anonymous URL holder: every participant signs in,
+and a meeting link gives an outsider a **guest grant**, never a way in
+without a name (ruling 1; suite ruling 15); a guest waits until an
+admitted member lets them in (ruling 9, §3.3).
+
+Access is a magic link gated by `MEET_ALLOWED_ADDRESSES`
+(`internal/meet/config.go`), with optional Home sign-in when an operator
+sets all four `MEET_HOME_*` settings (§7.1.1) — Home only *links* an
+existing member after independent local sign-in; it never creates a
+person or grants membership. Unlike Docs' §8.1, the spec states no
+passkey ruling of its own. **[inferred — confirm]** the team is assumed
+small enough that an address allowlist is appropriate; the spec states
+no target size.
## Product Purpose
-Meet is **video calling for one Oficina team**: a member makes a meeting, sends one link, and
-the people who follow it set up camera and microphone, wait to be let in if not on the team,
-and talk (preamble). "The instance carries the pages, the sign-in, the signalling and the
-in-call chat, and never a frame of media" (§0.1) — falsifiable: publish the SQLite file,
-its WAL, and every log line, and the worst anyone learns is the shape of the team's meetings
-and who minted what, never a title, chat text, or signalling payload, enforced by a WAL-grep
-test and a captured-logger test, not merely asserted (§0.1).
+Meet is **video calling for one Oficina team**: a member makes a
+meeting, sends one link, and the people who follow it set up camera and
+microphone, wait to be let in if not on the team, and talk (preamble).
+"The instance carries the pages, the sign-in, the signalling and the
+in-call chat, and never a frame of media" (§0.1) — falsifiable: publish
+the SQLite file, its WAL, and every log line, and the worst anyone
+learns is the shape of the team's meetings and who minted what, never a
+title, chat text, or signalling payload, enforced by a WAL-grep test and
+a captured-logger test, not merely asserted (§0.1).
## Positioning
1. **The claim is made per process, not once** (§0.1): Meet's own
- process never touches a media packet; the platform TURN relay forwards encrypted DTLS-SRTP
- it cannot read; a future platform SFU decrypts to forward and gets its own spec revision
- when it ships (§5.7).
+ process never touches a media packet; the platform TURN relay
+ forwards encrypted DTLS-SRTP it cannot read; a future platform SFU
+ decrypts to forward and gets its own spec revision when it ships
+ (§5.7).
2. **No relay of its own, by ruling** (suite ruling 6, §5.2): present
- `CARLOS_RELAY` mints ICE credentials per call; absent means host candidates only, stated
- before the join button (§5.5).
+ `CARLOS_RELAY` mints ICE credentials per call; absent means host
+ candidates only, stated before the join button (§5.5).
3. **Two authorities, not one, for a room.** *Standing* (§2.3, computed
- per request, never stored) decides who may **enter**; a *participation* (§2.4, hub state)
- decides who may **continue** — an admitted guest keeps talking past its grant's expiry
- while a member who loses standing is dropped at once (ruling 14).
+ per request, never stored) decides who may **enter**; a
+ *participation* (§2.4, hub state) decides who may **continue** — an
+ admitted guest keeps talking past its grant's expiry while a member
+ who loses standing is dropped at once (ruling 14).
## Operating Context
-- **One Go binary per instance**, `CGO_ENABLED=0`, deployed on CARLOS
- as a **unit route** (§7.3). Native Apple bindings under `native/` are the one place cgo is
- used, isolated in `native/go.mod` (`AGENTS.md`).
+- **One Go binary per instance**, `CGO_ENABLED=0`, deployed on CARLOS as
+ a **unit route** (§7.3). Native Apple bindings under `native/` are the
+ one place cgo is used, isolated in `native/go.mod` (`AGENTS.md`).
- **Routes today** (`internal/meet/app.go`): `GET /`, `POST /meetings`;
- `GET /j/{token}` (outside the session group); the holding screen and room; signalling
- (`stream`, `signal`, `ice`); `POST /m/{sqid}/share` (2026-09-12); four confirm/POST pairs;
- Settings and agent tokens; sign-in including optional Home routes; native handoff endpoints; a
- bearer-only `/api/v1/*` group (`api.go`) outside CSRF and the session group. `GET /healthz`/`GET
- /api/version` answer from rastrillo itself, outside app middleware.
-- **Scriptless, with one named exception: browser media
- participation.** Every other screen — the list, creating a meeting, a link shown once,
- every confirm page, sign-in, `/welcome`, the agent token page, the Share page and the
- Home account-link screens (§7.1.1), and the server-rendered parts of the holding screen
- and room — works with scripts off (§9; `AGENTS.md`'s UX line carries the same exception;
+ `GET /j/{token}` (outside the session group); the holding screen and
+ room; signalling (`stream`, `signal`, `ice`); `POST /m/{sqid}/share`
+ (2026-09-12); four confirm/POST pairs; Settings and agent tokens;
+ sign-in including optional Home routes; native handoff endpoints; a
+ bearer-only `/api/v1/*` group (`api.go`) outside CSRF and the session
+ group. `GET /healthz`/`GET /api/version` answer from rastrillo itself,
+ outside app middleware.
+- **Scriptless, with one named exception: browser media participation.**
+ Every other screen — the list, creating a meeting, a link shown once,
+ every confirm page, sign-in, `/welcome`, the agent token page, the
+ Share page and the Home account-link screens (§7.1.1), and the
+ server-rendered parts of the holding screen and room — works with
+ scripts off (§9; `AGENTS.md`'s UX line carries the same exception;
full list below).
- **Nothing stored beyond a meeting's bones.** Rooms, peers, admission,
- chat and presence live in an in-process hub and die with the process (§2.2); a restart drops
- every live call (§4.5). Stored: `persons`, `meetings` (sealed title, `internal/meet/seal/`),
- `meeting_links` (hash-indexed plus a sealed copy for sharing since 2026-09-12), `pending_joins`
- (a keyed address fingerprint, never plaintext), `guest_grants`, `agents` (a member's minted
- principal; the token itself is never stored, only its SHA-256) (models.go; §2.1).
+ chat and presence live in an in-process hub and die with the process
+ (§2.2); a restart drops every live call (§4.5). Stored: `persons`,
+ `meetings` (sealed title, `internal/meet/seal/`), `meeting_links`
+ (hash-indexed plus a sealed copy for sharing since 2026-09-12),
+ `pending_joins` (a keyed address fingerprint, never plaintext),
+ `guest_grants`, `agents` (a member's minted principal; the token
+ itself is never stored, only its SHA-256) (models.go; §2.1).
## Capabilities and Constraints
-**Shipped:** meeting creation and listing; a link shown once, replaced or shared via `POST
-/m/{sqid}/share`; sign-in with the pending-join exception admitting an outsider only while
-holding a live link (ruling 12, §3.2); optional Home account-linking (§7.1.1); the holding
-screen and a P2P mesh room capped at four with perfect negotiation, resume, and a replay ring
-(§4); relay-minted ICE credentials renewed before deadline (§5.2, §5.3); ephemeral chat
-and screen share (§6, §5.4); a bearer-token drive surface for agents re-checking operator
-membership on every request (§8, §15.17); native Mac and iPhone/iPad apps sharing the Go
+**Shipped:** meeting creation and listing; a link shown once, replaced
+or shared via `POST /m/{sqid}/share`; sign-in with the pending-join
+exception admitting an outsider only while holding a live link (ruling
+12, §3.2); optional Home account-linking (§7.1.1); the holding screen
+and a P2P mesh room capped at four with perfect negotiation, resume, and
+a replay ring (§4); relay-minted ICE credentials renewed before deadline
+(§5.2, §5.3); ephemeral chat and screen share (§6, §5.4); a bearer-token
+drive surface for agents re-checking operator membership on every
+request (§8, §15.17); native Mac and iPhone/iPad apps sharing the Go
protocol core (`native/README.md`).
-**Deliberately not built yet** (§11, as amended): see "What is deliberately not here" below
-for the full, cited list.
+**Deliberately not built yet** (§11, as amended): see "What is
+deliberately not here" below for the full, cited list.
**Binding technical constraints:**
- **Media never touches Meet's own process** (§0.1); no handler accepts
- a media packet. **`scope.Owned`, no bound request body**: every user-owned query goes through
- it, reads and writes alike; a row that isn't yours 404s, never 403s (`AGENTS.md`).
+ a media packet. **`scope.Owned`, no bound request body**: every
+ user-owned query goes through it, reads and writes alike; a row that
+ isn't yours 404s, never 403s (`AGENTS.md`).
- **Standing is computed, never cached** — every request, every
- 15-second hub tick (§2.3, §2.4). A shipped migration is never edited. The agent API's rate
- limit keys on the resolved agent, not the client address, deliberately (§15.17; see below).
+ 15-second hub tick (§2.3, §2.4). A shipped migration is never edited.
+ The agent API's rate limit keys on the resolved agent, not the client
+ address, deliberately (§15.17; see below).
- **`make race`** for any change to `internal/meet/hub` or the stream —
- the one place `CGO_ENABLED=1` is allowed, for a test binary only. **`make browser`** needs
- UDP, which the Claude Code bash sandbox blocks (`AGENTS.md`).
+ the one place `CGO_ENABLED=1` is allowed, for a test binary only.
+ **`make browser`** needs UDP, which the Claude Code bash sandbox
+ blocks (`AGENTS.md`).
## Brand Commitments
- **Name:** Meet, `amadan.net/oficina/meet`, first served at
- `meet.oficina.works` (preamble; ruling 2 chose `oficina/meet` over `oficina/conferencia`). Built
- beside Docs, Calendar, Slides and the suite doctrine, consuming a decision made there rather
- than restating it (preamble, §0.2).
+ `meet.oficina.works` (preamble; ruling 2 chose `oficina/meet` over
+ `oficina/conferencia`). Built beside Docs, Calendar, Slides and the
+ suite doctrine, consuming a decision made there rather than restating
+ it (preamble, §0.2).
- **The theme is Estilo's**, adopted at whatever Estilo holds when each
- slice lands. No tag exists (`git ls-remote --tags` on `oficina/estilo` was empty on 2026-09-14);
- consumed at a pseudo-version, already adopted by the Home integration on main. **[inferred
- — confirm]** the exact commit at any later date.
+ slice lands. No tag exists (`git ls-remote --tags` on `oficina/estilo`
+ was empty on 2026-09-14); consumed at a pseudo-version, already
+ adopted by the Home integration on main. **[inferred — confirm]** the
+ exact commit at any later date.
- **The chat panel is Meet's contribution to Estilo** — "one
- self-contained template partial and one script file so a second app can adopt it," the suite's
- first chat, built to be taken (§6).
+ self-contained template partial and one script file so a second app
+ can adopt it," the suite's first chat, built to be taken (§6).
- **Voice: [inferred — confirm].** Copy review was waived for this
- documentation round (steering on `oficina/suite` discussion 5), so shipped strings were not
- checked against a review record.
+ documentation round (steering on `oficina/suite` discussion 5), so
+ shipped strings were not checked against a review record.
## Evidence on Hand
- **Spec:** revision 9 plus the 2026-09-10 and 2026-09-12 amendments,
- 2100+ lines, six rounds of Codex review (§14). **Plans:** `docs/superpowers/plans/`, one
- programme (slices 2–6) plus a plan per slice. **Slice records:** `docs/superpowers/reviews/`,
- one per slice 2–5 (room, relay, chat/screen-share, drive surface), each an index into the
- spec's own §15 rulings for that slice.
+ 2100+ lines, six rounds of Codex review (§14). **Plans:**
+ `docs/superpowers/plans/`, one programme (slices 2–6) plus a plan per
+ slice. **Slice records:** `docs/superpowers/reviews/`, one per slice
+ 2–5 (room, relay, chat/screen-share, drive surface), each an index
+ into the spec's own §15 rulings for that slice.
- **Native:** `native/README.md`, `native/DESIGN.md`,
- `native/VERIFICATION.md` — build, gates, stated acceptance limits (no real-device pass
- claimed for camera permissions, audio-device switching, sleep/wake, or Home passkeys).
+ `native/VERIFICATION.md` — build, gates, stated acceptance limits (no
+ real-device pass claimed for camera permissions, audio-device
+ switching, sleep/wake, or Home passkeys).
- **What does not exist and must not be invented:** customers,
- testimonials, benchmarks, pricing, usage numbers, launch date, press, or a production promotion
- from this slice — edge (`meet.oficina.works`) is promoted by hand by Paul, separately from
+ testimonials, benchmarks, pricing, usage numbers, launch date, press,
+ or a production promotion from this slice — edge
+ (`meet.oficina.works`) is promoted by hand by Paul, separately from
documentation (slice 6 plan, "What slice 6 was").
## Product Principles
1. **State the degradation rather than let it be discovered.** The
- no-relay floor says calls usually connect only on the same network (§5.5); §0.3 states what
- the privacy claim excludes (signalling in flight, a replay ring of up to 32 messages for 30
- seconds, a link as a bearer); the mail-budget residue is written down, not claimed away (§3.2).
+ no-relay floor says calls usually connect only on the same network
+ (§5.5); §0.3 states what the privacy claim excludes (signalling in
+ flight, a replay ring of up to 32 messages for 30 seconds, a link as
+ a bearer); the mail-budget residue is written down, not claimed away
+ (§3.2).
2. **A verification that cannot distinguish "no" from "I can't see it"
- is not a verification.** The §0.1 claim is enforced by grepping raw SQLite bytes and the
- WAL and capturing the logger during a real call; `make browser` asserts a received track by
- `ontrack` and frame count, never a label (§13).
+ is not a verification.** The §0.1 claim is enforced by grepping raw
+ SQLite bytes and the WAL and capturing the logger during a real call;
+ `make browser` asserts a received track by `ontrack` and frame count,
+ never a label (§13).
3. **Two authorities are named because one would be wrong in some
- direction** (§2.3–§2.4, ruling 14): a single authority either drops an admitted guest
- at the grant's two-hour mark or lets an expired grant re-enter.
+ direction** (§2.3–§2.4, ruling 14): a single authority either drops
+ an admitted guest at the grant's two-hour mark or lets an expired
+ grant re-enter.
4. **A contract is written for the next client, not the current one.**
- Signalling, `/ice` and the meeting API are contracts a native client could speak (ruling 15,
- §8.4); the native apps are that ruling paying off.
+ Signalling, `/ice` and the meeting API are contracts a native client
+ could speak (ruling 15, §8.4); the native apps are that ruling paying
+ off.
5. **Every write names its real actor**, `person:<id>` or `agent:<id>`
(§7.2, doctrine ruling 9).
## Accessibility & Inclusion
-`AGENTS.md`'s vendored UI conventions apply: labelled fields with one error envelope, destructive
-actions on their own confirm-page URL and never a modal (`ui/confirm-form` — §3.1, §3.5,
-§4.2), state shown with a text label and not colour alone, and empty states that say what the
+`AGENTS.md`'s vendored UI conventions apply: labelled fields with one
+error envelope, destructive actions on their own confirm-page URL and
+never a modal (`ui/confirm-form` — §3.1, §3.5, §4.2), state shown with a
+text label and not colour alone, and empty states that say what the
screen is for.
-**The scriptless floor is the accessibility constraint this app adds beyond that shared list**,
-and it is exact: reaching a camera and another browser needs script; nothing else does. With
-scripts off the holding screen's join button becomes one paragraph saying a call needs JavaScript
-and everything else on the page works without it (§3.3); the room does the same (§3.4).
+**The scriptless floor is the accessibility constraint this app adds
+beyond that shared list**, and it is exact: reaching a camera and
+another browser needs script; nothing else does. With scripts off the
+holding screen's join button becomes one paragraph saying a call needs
+JavaScript and everything else on the page works without it (§3.3); the
+room does the same (§3.4).
-No target locale count or WCAG conformance level is stated in the Meet spec or
-`AGENTS.md`. **[inferred — confirm]**: Docs states "WCAG 2.2 AA, no exemptions" for itself;
-nothing here says the same target binds Meet, and this file does not assume it does.
+No target locale count or WCAG conformance level is stated in the Meet
+spec or `AGENTS.md`. **[inferred — confirm]**: Docs states "WCAG 2.2 AA,
+no exemptions" for itself; nothing here says the same target binds Meet,
+and this file does not assume it does.
## What is deliberately not here
- **Scriptless media** — the one named exception to the scriptless
- floor: reaching a camera and another browser needs script; every other screen, including the
- server-rendered parts of the holding screen and the room, works with scripts off (spec §9;
- `AGENTS.md`'s UX-conventions line carries the same exception in the same words).
+ floor: reaching a camera and another browser needs script; every other
+ screen, including the server-rendered parts of the holding screen and
+ the room, works with scripts off (spec §9; `AGENTS.md`'s
+ UX-conventions line carries the same exception in the same words).
- **No STUN or TURN server of Meet's own.** ICE servers come from the
- platform relay, present or not at all — never a server Meet names itself (suite ruling 6,
- §5.2, §5.5).
+ platform relay, present or not at all — never a server Meet names
+ itself (suite ruling 6, §5.2, §5.5).
- **No SFU, and the room is capped at four admitted peers** — not a
- claimed product limit, the mesh's own (ruling 5), lifted only when a platform SFU that
- decrypts to forward ships, in its own spec revision, not a quiet cap change (§5.7, §0.3,
- §11 "rooms above four").
+ claimed product limit, the mesh's own (ruling 5), lifted only when a
+ platform SFU that decrypts to forward ships, in its own spec revision,
+ not a quiet cap change (§5.7, §0.3, §11 "rooms above four").
- **No client-address limiter on the drive surface.** The agent API's
- per-agent rate limit (60/min) keys on the resolved agent, not the client address: behind the
- platform's relay socket every request can share one address, and an address-keyed limiter
- would let one flood take out every real agent at once (§15.17).
+ per-agent rate limit (60/min) keys on the resolved agent, not the
+ client address: behind the platform's relay socket every request can
+ share one address, and an address-keyed limiter would let one flood
+ take out every real agent at once (§15.17).
- **No passkeys ruling of its own.** Unlike Docs §8.1, the spec states
no instance-local-passkey decision. **[inferred — confirm]**.
- **Chat is never stored.** A `chat` message fans out to admitted peers
- and renders client-side; not replayed to a peer who joins or resumes later, gone with the tab
- (ruling 8, §6).
+ and renders client-side; not replayed to a peer who joins or resumes
+ later, gone with the tab (ruling 8, §6).
- **No recurring meetings, Calendar integration, recording, attendance
- records, reactions, dial-in, captions, multi-host signalling, push-to-talk, or virtual
- backgrounds, and no way to deny a knock** — a guest who isn't admitted simply waits or leaves
- (§11).
+ records, reactions, dial-in, captions, multi-host signalling,
+ push-to-talk, or virtual backgrounds, and no way to deny a knock** — a
+ guest who isn't admitted simply waits or leaves (§11).
- **No native client sign-in method of its own** — a native client
- authenticates through the same Home/local sign-in the browser uses, handed off through a
- five-minute, one-use authorization; no separate bearer or token scheme for native apps (§8.4,
- `native/README.md`). **No attendance claim, ever**: §0.3 says Meet does not record who was
- in a call or for how long; an earlier revision implied one and the spec dropped the implication
- rather than building the model.
+ authenticates through the same Home/local sign-in the browser uses,
+ handed off through a five-minute, one-use authorization; no separate
+ bearer or token scheme for native apps (§8.4, `native/README.md`).
+ **No attendance claim, ever**: §0.3 says Meet does not record who was
+ in a call or for how long; an earlier revision implied one and the
+ spec dropped the implication rather than building the model.
---
-*See also:* the design spec, `README.md`, `AGENTS.md`, and the slice records under
-`docs/superpowers/reviews/`.
+*See also:* the design spec, `README.md`, `AGENTS.md`, and the slice
+records under `docs/superpowers/reviews/`.
+