The Guest holds the Seat: the server mints it, the browser keeps and sends it, the Seat token is gone #372

Closed
opened 2026-10-11 16:26:18 +00:00 by root · 25 comments
root commented 2026-10-11 16:26:18 +00:00 (Migrated from gitlab.core-project.space)

Imported from GitHub amaurycoudr/chess-project#372, opened by @amaurycoudr on 2026-10-11 16:26 UTC.


Parent

#355

What to build

A browser proves its Seat with one Guest, in every Room, instead of a Seat token per Room (ADR 0034).

When a browser creates a Room or takes a Seat without a Guest, the server mints one: at least 128 random bits, URL-safe, through a constructor option. The response says which Guest it minted, or that it minted none, and that response is the only time the secret crosses the wire. A refused take (NO_FREE_SEAT) mints nothing, so a Spectator never gets a Guest. The API's RPC context reads the Guest header and hashes the secret with SHA-256 on arrival. From then on the caller's occupant is a union with one variant today, a Guest known by its hash. Resolving the caller stays synchronous and reads no database. An unknown secret is a Guest with nothing to its name, never a validation error.

The Room keys each occupant by its Guest and is always handed one; it no longer mints anything. A Guest that already holds a Seat in a Room gets that Seat back on a second take, never the other one.

The front keeps the Guest under one localStorage key of its own. It sends the Guest in its header on every call and replaces it with whatever a response minted. The store sits where both room/ and a future games/ vertical may import it. Entering a Room always calls seat.take, still deduplicated per Room code. The answer either hands back the held Seat, takes a free one, or refuses, and a refusal makes the browser a Spectator. Leaving forgets nothing. If storage throws, play still works, but a reload loses the Seat.

The Seat token is removed everywhere: its contract schema and header, the token in the create and take output, the per-Room front storage, and the API's handling of its header. Leftover per-Room keys in browsers are ignored. Comments that cite ADR 0016 are repointed to ADR 0034 or dropped. A Player sees no difference.

Seam: the Room with its fixtures (seating by Guest); the RPC wire with the in-process app (minting, acting for a Seat); the Room view tests with their harness and MSW (keeping and sending the Guest); e2e (the existing reload and second-tab specs kept green).

Acceptance criteria

  • room.create and seat.take with no Guest mint one and say so. With a known or unknown Guest, they mint nothing.
  • A refused seat.take (NO_FREE_SEAT) mints no Guest (story 3).
  • A call with no header, or with another browser's Guest missing, cannot move, offer, resign or leave for a Seat. Knowing the Room code is not enough (story 5).
  • A Guest taking again in a Room where it sits gets the same Seat and no new Guest (story 6).
  • The secret is hashed in the RPC context. Nothing past the context sees it, and resolving the caller does no I/O.
  • The front stores the Guest under one key, sends it in a header (never in a URL) on every call including refetches, and replaces it with a minted one (story 2).
  • Entering a Room always calls seat.take once per Room code, and a refusal lands the browser as a Spectator.
  • Front storage that throws on read or write still lets the browser play.
  • The existing reload and second-tab e2e specs pass unchanged in what they assert (story 4).
  • No Seat token schema, header, output field, front storage or ADR 0016 comment remains.
  • The PR body proposes rewording the three rule files that still name the Seat token (the API header rule, the e2e rule on each Player's token, the contracts' branded-identifier example). They are not edited here.
> _Imported from GitHub [amaurycoudr/chess-project#372](https://github.com/amaurycoudr/chess-project/issues/372), opened by [@amaurycoudr](https://github.com/amaurycoudr) on 2026-10-11 16:26 UTC._ <!-- github-issue:372 --> --- ## Parent #355 ## What to build A browser proves its **Seat** with one **Guest**, in every **Room**, instead of a **Seat token** per **Room** (ADR 0034). When a browser creates a **Room** or takes a **Seat** without a **Guest**, the server mints one: at least 128 random bits, URL-safe, through a constructor option. The response says which **Guest** it minted, or that it minted none, and that response is the only time the secret crosses the wire. A refused take (`NO_FREE_SEAT`) mints nothing, so a **Spectator** never gets a **Guest**. The API's RPC context reads the **Guest** header and hashes the secret with SHA-256 on arrival. From then on the caller's occupant is a union with one variant today, a **Guest** known by its hash. Resolving the caller stays synchronous and reads no database. An unknown secret is a **Guest** with nothing to its name, never a validation error. The **Room** keys each occupant by its **Guest** and is always handed one; it no longer mints anything. A **Guest** that already holds a **Seat** in a **Room** gets that **Seat** back on a second take, never the other one. The front keeps the **Guest** under one `localStorage` key of its own. It sends the **Guest** in its header on every call and replaces it with whatever a response minted. The store sits where both `room/` and a future `games/` vertical may import it. Entering a **Room** always calls `seat.take`, still deduplicated per **Room code**. The answer either hands back the held **Seat**, takes a free one, or refuses, and a refusal makes the browser a **Spectator**. Leaving forgets nothing. If storage throws, play still works, but a reload loses the **Seat**. The **Seat token** is removed everywhere: its contract schema and header, the token in the create and take output, the per-**Room** front storage, and the API's handling of its header. Leftover per-**Room** keys in browsers are ignored. Comments that cite ADR 0016 are repointed to ADR 0034 or dropped. A Player sees no difference. Seam: the `Room` with its fixtures (seating by **Guest**); the RPC wire with the in-process app (minting, acting for a **Seat**); the **Room** view tests with their harness and MSW (keeping and sending the **Guest**); e2e (the existing reload and second-tab specs kept green). ## Acceptance criteria - [ ] `room.create` and `seat.take` with no **Guest** mint one and say so. With a known or unknown **Guest**, they mint nothing. - [ ] A refused `seat.take` (`NO_FREE_SEAT`) mints no **Guest** (story 3). - [ ] A call with no header, or with another browser's **Guest** missing, cannot move, offer, resign or leave for a **Seat**. Knowing the **Room code** is not enough (story 5). - [ ] A **Guest** taking again in a **Room** where it sits gets the same **Seat** and no new **Guest** (story 6). - [ ] The secret is hashed in the RPC context. Nothing past the context sees it, and resolving the caller does no I/O. - [ ] The front stores the **Guest** under one key, sends it in a header (never in a URL) on every call including refetches, and replaces it with a minted one (story 2). - [ ] Entering a **Room** always calls `seat.take` once per **Room code**, and a refusal lands the browser as a **Spectator**. - [ ] Front storage that throws on read or write still lets the browser play. - [ ] The existing reload and second-tab e2e specs pass unchanged in what they assert (story 4). - [ ] No **Seat token** schema, header, output field, front storage or ADR 0016 comment remains. - [ ] The PR body proposes rewording the three rule files that still name the **Seat token** (the API header rule, the e2e rule on each Player's token, the contracts' branded-identifier example). They are not edited here.
root commented 2026-10-11 16:26:18 +00:00 (Migrated from gitlab.core-project.space)

GitHub issue relations, imported as "related" links (GitLab CE has no sub-issues or blocking links).

> _GitHub issue relations, imported as "related" links (GitLab CE has no sub-issues or blocking links)._ <!-- github-relations --> - Parent (sub-issue of): #355 - Blocked by: #371, #370 - Blocking: #373
root commented 2026-10-13 08:39:45 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #373

mentioned in issue #373
root commented 2026-10-13 08:41:21 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #355

mentioned in issue #355
root commented 2026-10-13 08:41:24 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #370

mentioned in issue #370
root commented 2026-10-13 08:41:24 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #371

mentioned in issue #371
root commented 2026-10-13 08:42:26 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #355

marked this issue as related to #355
root commented 2026-10-13 08:42:34 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #370

marked this issue as related to #370
root commented 2026-10-13 08:42:34 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #371

marked this issue as related to #371
root commented 2026-10-13 08:42:34 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #373

marked this issue as related to #373
root commented 2026-10-13 14:34:05 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 8ad1fd91fe

mentioned in commit 8ad1fd91feaaa4c662dc6de37211803a2592d6dc
root commented 2026-10-13 14:34:05 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit d5cf28a684

mentioned in commit d5cf28a684edded345fd2c9a7951dab33a299ab2
root commented 2026-10-13 14:34:05 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit c56a5f5454

mentioned in commit c56a5f54542789e32ad9ccd18b8d2c7647fcd57b
root commented 2026-10-13 14:34:05 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit d6459084e7

mentioned in commit d6459084e7ac41fd78d70bd7542611d1c496343a
root commented 2026-10-13 14:34:05 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit d5b42b7f11

mentioned in commit d5b42b7f115b57cf8fbcaef5b4142701b76cc769
root commented 2026-10-13 14:34:16 +00:00 (Migrated from gitlab.core-project.space)

mentioned in merge request !515

mentioned in merge request !515
root commented 2026-10-14 07:52:22 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 941c8132bd

mentioned in commit 941c8132bdcce61ba98a8649b74fb2b755b14d75
root commented 2026-10-14 07:52:23 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 7d0a5a725e

mentioned in commit 7d0a5a725eda1896cbe81f2926e988cbc7621c8c
root commented 2026-10-14 07:52:23 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 66524f0f45

mentioned in commit 66524f0f45e8142d27205fe7cbc00ebe3aa970e8
root commented 2026-10-14 07:52:23 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit ff47973c19

mentioned in commit ff47973c1977b486690f0696542686b78af8a06b
root commented 2026-10-14 07:52:24 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 1696cd91ef

mentioned in commit 1696cd91ef410552b021bfdcd81d7f047ecd4c5c
root commented 2026-10-14 09:31:43 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit f453e2ce3f

mentioned in commit f453e2ce3f28c25fb44a56bc38e101102d8d708e
root commented 2026-10-14 09:31:43 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit c1eb8e46e2

mentioned in commit c1eb8e46e2d4426e0813ae851fd34ed751325125
root commented 2026-10-14 09:31:43 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 084b9e5706

mentioned in commit 084b9e570619d59578deafd4f5efb9db9f50b265
root commented 2026-10-14 09:31:44 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit d7ee32c058

mentioned in commit d7ee32c0580feb5b3e1453a5d25a3d684bdd804d
root commented 2026-10-14 09:31:44 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 309352130c

mentioned in commit 309352130c16a3f26e31df154d789f79cdb89a49
root (Migrated from gitlab.core-project.space) closed this issue 2026-10-14 09:31:57 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
amaurycoudr/chess-project#372
No description provided.