[Foundry P1] Pair a Foundry world: pairing codes, per-GM tokens, the Quest Board module served by each server (D333–D336) #5

Open
opened 2026-06-23 00:42:31 +00:00 by rbrooks · 1 comment
Owner

Rewritten 2026-11-07 for the module design (D333–D338, #143 closed, #4 passed 7/7). The old scope (a server URL, an encrypted API key and a system selector) is gone: there is no URL field and no API key, so no SSRF exemption is needed.

The contract: docs/design/foundry-connection.md (branch feat/5-foundry-connect).

Scope

  • Pairing: a GM gets an 8-character code from Settings → Foundry (10 minutes, used once, Redis DB 0). Pairing happens over the module's socket. Only a Foundry Gamemaster can pair. One world per campaign. Any GM can pair; the owner can unpair the world (D336); a GM can remove a pairing they made.
  • Tokens: one per Foundry GM, stored as sha256 hashes, never logged, revocable.
  • The module (quest-board): served by each Quest Board server as /api/foundry/module.json and a zip that carries that server's wss:// address (D334), so a GM never types a URL. GM-only settings menu (#4 finding 1).
  • Tables: foundry_worlds and foundry_tokens, one migration.
  • nginx: upgrade headers for /api/foundry/ws.
  • Settings → Foundry (CAMP-16): the code, the paired world (name, system, core version), the paired GMs, Unpair. Drawn on a prototype page first; IS-268–271 are replaced.

Done when

A GM installs the module from their server's manifest URL, pairs The Amberwake Accord on foundry-dev with a code, and Settings → Foundry shows the world. The owner can unpair it, which drops the socket. A player can't pair. The token never appears in logs or responses after the one paired message.

**Rewritten 2026-11-07 for the module design** (D333–D338, #143 closed, #4 passed 7/7). The old scope (a server URL, an encrypted API key and a system selector) is gone: there is no URL field and no API key, so no SSRF exemption is needed. **The contract:** `docs/design/foundry-connection.md` (branch `feat/5-foundry-connect`). ## Scope - **Pairing:** a GM gets an 8-character code from Settings → Foundry (10 minutes, used once, Redis DB 0). Pairing happens over the module's socket. Only a Foundry Gamemaster can pair. One world per campaign. Any GM can pair; the owner can unpair the world (D336); a GM can remove a pairing they made. - **Tokens:** one per Foundry GM, stored as sha256 hashes, never logged, revocable. - **The module (`quest-board`):** served by each Quest Board server as `/api/foundry/module.json` and a zip that carries that server's `wss://` address (D334), so a GM never types a URL. GM-only settings menu (#4 finding 1). - **Tables:** `foundry_worlds` and `foundry_tokens`, one migration. - **nginx:** upgrade headers for `/api/foundry/ws`. - **Settings → Foundry (CAMP-16):** the code, the paired world (name, system, core version), the paired GMs, Unpair. Drawn on a prototype page first; IS-268–271 are replaced. ## Done when A GM installs the module from their server's manifest URL, pairs The Amberwake Accord on foundry-dev with a code, and Settings → Foundry shows the world. The owner can unpair it, which drops the socket. A player can't pair. The token never appears in logs or responses after the one `paired` message.
claude-bot changed title from [Foundry P1] Per-campaign Foundry connection settings (URL, encrypted API key, system selector) to [Foundry P1] Pair a Foundry world: pairing codes, per-GM tokens, the Quest Board module served by each server (D333–D336) 2026-11-07 15:54:44 +00:00
Collaborator

Live test on foundry-dev passed (2026-11-09, owner at the browser). Foundry 14.369, pf2e 8.7.0, The Amberwake Accord. The backend under test was the feat/9-foundry-push code: a throwaway on the qb-foundry-test endpoint, through int-proxy over wss://, with the D337 stand-in stopped for the test and restarted afterwards.

Check Result
Pair as Ryan with a code; the GM-only "now paired" whisper; the marker ✅
A second GM (Test-GM) pairs from another Quest Board GM's code (Sam) ✅
One holder: Test-GM on "Waiting (held by Ryan)", handoff in 2–3 s when Ryan's window closed ✅
Player2: no settings entry, no marker, no connection (only two sockets ever opened) ✅
Send Brenna Hask: level 3 pf2e NPC with AC 18, HP 40, the attributes, Perception +9, speed 25 ✅
Audience: public notes hold the summary and table text; private notes hold only the :::gm line ✅
Sent while the world was closed: queued (1 waiting), delivered the second a GM reopened, same actor, "updated" whisper ✅

Fixed during the test (on #1369):

  1. HP on update: pf2e clamped the new value to the old max (40/45). Now full stays full and damage is kept; checked live at 50/50, then 30/55.
  2. Speed in the notes: "Speed: 25 feet" was repeated in them.
  3. Doubled message: "not paired" showed twice before a GM had paired.
  4. Waiting form: a waiting GM's form said "not paired yet".
  5. Marker: cut off ("Quest ..."); it's now its own row above Latency/FPS, reading "Quest Board: Connected", checked live.

Found, still to do: a manifest install from foundry-dev fails ("Fetch Failed"), because the foundry-dev container's Docker DNS can't resolve *.lan.ryanbrooks.dev (by IP it reaches int-proxy fine). For the test, the module was copied into Data/modules/quest-board with the owner's OK. The real fix is DNS for that container in iac-repo.

**Live test on foundry-dev passed (2026-11-09, owner at the browser).** Foundry 14.369, pf2e 8.7.0, The Amberwake Accord. The backend under test was the `feat/9-foundry-push` code: a throwaway on the `qb-foundry-test` endpoint, through int-proxy over `wss://`, with the D337 stand-in stopped for the test and restarted afterwards. | Check | Result | |---|---| | Pair as Ryan with a code; the GM-only "now paired" whisper; the marker | ✅ | | A second GM (Test-GM) pairs from another Quest Board GM's code (Sam) | ✅ | | One holder: Test-GM on "Waiting (held by Ryan)", handoff in 2–3 s when Ryan's window closed | ✅ | | Player2: no settings entry, no marker, no connection (only two sockets ever opened) | ✅ | | Send Brenna Hask: level 3 pf2e NPC with AC 18, HP 40, the attributes, Perception +9, speed 25 | ✅ | | Audience: public notes hold the summary and table text; private notes hold only the `:::gm` line | ✅ | | Sent while the world was closed: queued (1 waiting), delivered the second a GM reopened, same actor, "updated" whisper | ✅ | **Fixed during the test (on #1369):** 1. **HP on update:** pf2e clamped the new value to the old max (40/45). Now full stays full and damage is kept; checked live at 50/50, then 30/55. 2. **Speed in the notes:** "Speed: 25 feet" was repeated in them. 3. **Doubled message:** "not paired" showed twice before a GM had paired. 4. **Waiting form:** a waiting GM's form said "not paired yet". 5. **Marker:** cut off ("Quest ..."); it's now its own row above Latency/FPS, reading "Quest Board: Connected", checked live. **Found, still to do:** a manifest install from foundry-dev fails ("Fetch Failed"), because the `foundry-dev` container's Docker DNS can't resolve `*.lan.ryanbrooks.dev` (by IP it reaches int-proxy fine). For the test, the module was copied into `Data/modules/quest-board` with the owner's OK. The real fix is DNS for that container in iac-repo.
Sign in to join this conversation.
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#5
No description provided.