aboutsummaryrefslogtreecommitdiffstats
path: root/CLAUDE.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-17 17:38:14 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-17 17:38:14 +0200
commitf23e8e51e4b8bc03d6109081fe048af1782e1f4e (patch)
tree29772398ef6cd488d50b032358b468eece0f2add /CLAUDE.md
parent6562665c80f96f30fef95af83a0abcf71f41795f (diff)
downloadmeshbay-f23e8e51e4b8bc03d6109081fe048af1782e1f4e.tar.gz
docs: sessions renew themselves, and two faults of the same shape0.5
USERGUIDE said an hour in five places and presented renewal as something the reader does with curl. Both are now wrong: it is four hours, the web app renews for itself, and the endpoint rotates — so anyone driving it by hand has to store the refresh token that comes back, or their next call revokes the family. Also corrects what the token's life actually bounds. It is not how long a revocation takes: the hub reloads the account on every request and refuses a suspended one at once, and it pushes signed revocations to nodes. What remains is a leaked token on an account still in good standing, which is the reason to keep the number small. Two lessons in CLAUDE.md. A rotated refresh token has to be stored or it is spent once. And an effect keyed on a value that used to be constant: the WebRTC dial listed `token` among its dependencies, harmless while a token only ever expired, fatal once the session renewed itself — it tore the connection down mid-handshake and the node waited for ever. That and the hook declared after its own dependency are the same shape, and worth naming as one: code that reads correctly on its own and is wrong against the component lifecycle.
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md26
1 files changed, 26 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 8e2eafa..76a7aa6 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -302,6 +302,30 @@ keypair bundle, or anything that looks like a user's public key.
reproduced the defect in one run. Model the environment, never the code under
test
+- **A refresh token that rotates must be stored, or it is spent once.** The hub
+ revokes the refresh token presented, returns a replacement, and treats a
+ revoked one presented again as theft — revoking the whole family. The SPA kept
+ only the access token out of that response, so renewal worked once and the
+ second attempt destroyed the session, which is why signing out and in was the
+ only cure. Nothing used the path at all: `hubFetch` reported 401 like any
+ other error, and watching a film is an hour in which the hub hears nothing,
+ because the video is WebRTC. Renew on a margin, on returning to the tab, and
+ on a 401 with a replay; coalesce concurrent renewals, or the second presents
+ what the first just spent and looks exactly like theft.
+ `tests/harness/session_harness.mjs` runs it against a hub that enforces
+ rotation — a lax stub would pass the broken client
+
+- **An effect keyed on a value that used to be constant.** The WebRTC dial
+ listed `token` among its dependencies. Harmless while a token only ever
+ expired; once the session renewed itself the string rotated, and the effect
+ tore the connection down and rebuilt it — worst at mount, where a stale token
+ is renewed exactly while ICE is negotiating, so the browser abandoned the
+ handshake and the node sat in `connecting` for ever. Depend on whether there
+ is a token, not which one, and read the live one where it is used. Before
+ making something vary that never varied before, grep the dependency arrays it
+ appears in — this and the hook-ordering fault above are the same shape: code
+ that is correct read on its own and wrong against the component lifecycle
+
- **A stylesheet does not tell you where anything lands.** The responsive tests
pinned numbers out of `style.css` and said in their own docstring that a
layout could not be measured because there was no browser in the suite. There
@@ -509,6 +533,8 @@ SFR residential Fedora 44 → meshbay.org OVH VPS:
| Seeking (node) | `webrtc_server.py` | `start` on `stream_req`; `-ss` **before** `-i` (index seek, not decode-and-discard), clamped away from the end, echoed in `stream_init` |
| Resume position | `static/app.js` | `readResumePosition` / `writeResumePosition` — localStorage, per file, per browser. No protocol, and nothing new learns what you watch |
| Layout, measured | `tests/harness/layout_probe.py` | Renders `style.css` in Chrome at any width and returns bounding boxes. Use it for layout, not `test_layout_responsive.py`, which only pins CSS values |
+| Session renewal (browser) | `static/app.js` | `refreshAccessToken` / `ensureFreshToken` — one writer (`setAuth`), one in-flight renewal, rotated refresh token stored. `hubFetch` renews on 401 and replays |
+| Token lifetimes (hub) | `meshbay_hub.config` | `[jwt] access_token_ttl` 4 h, `refresh_token_ttl` 30 days. **Production sets both in `~/.config/meshbay/hub.toml`** — changing the code default alone does nothing there |
## meshbay.org server (état cible)