From 73ad8e4eb566fe682107fa7e50ef624591199e99 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Tue, 15 Sep 2026 02:16:39 +0200 Subject: feat(hub): session lifetime is an admin setting, and a browser signs out when idle Browser idle sign-out (media playback counts as activity; not the desktop app), refresh idle window and maximum session length, in hours. Sign-out now revokes on the hub, and the profile has "sign out everywhere". Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XuNrwLf5EFWCMHzfoEvnpm --- CLAUDE.md | 2 +- docs/MESHBAY_DESIGN.md | 15 ++ packages/meshbay-hub/src/meshbay_hub/api/admin.py | 25 +++ packages/meshbay-hub/src/meshbay_hub/api/hub.py | 4 + packages/meshbay-hub/src/meshbay_hub/api/users.py | 97 ++++++++++- packages/meshbay-hub/src/meshbay_hub/config.py | 15 +- .../meshbay-hub/src/meshbay_hub/hub_settings.py | 40 +++++ .../src/meshbay_hub/static/admin-page.js | 34 ++++ packages/meshbay-hub/src/meshbay_hub/static/app.js | 94 +++++++---- .../src/meshbay_hub/static/hub-client.js | 18 +- .../meshbay-hub/src/meshbay_hub/static/idle.js | 77 +++++++++ .../src/meshbay_hub/static/locales/de.js | 10 ++ .../src/meshbay_hub/static/locales/en.js | 10 ++ .../src/meshbay_hub/static/locales/es.js | 10 ++ .../src/meshbay_hub/static/locales/fr.js | 10 ++ .../src/meshbay_hub/static/locales/it.js | 10 ++ .../src/meshbay_hub/static/locales/ja.js | 10 ++ .../src/meshbay_hub/static/locales/nl.js | 10 ++ .../src/meshbay_hub/static/locales/pl.js | 10 ++ .../src/meshbay_hub/static/locales/pt-BR.js | 10 ++ .../src/meshbay_hub/static/locales/zh-CN.js | 10 ++ .../src/meshbay_hub/static/profile-page.js | 26 +++ packages/meshbay-hub/tests/conftest.py | 2 +- .../meshbay-hub/tests/test_browser_idle_signout.py | 130 +++++++++++++++ .../meshbay-hub/tests/test_session_lifetime.py | 183 +++++++++++++++++++++ packages/meshbay-hub/tests/test_session_renewal.py | 17 +- .../tests/test_unauthenticated_surface.py | 1 + packaging/conf/hub.toml.example | 3 +- 28 files changed, 835 insertions(+), 48 deletions(-) create mode 100644 packages/meshbay-hub/src/meshbay_hub/static/idle.js create mode 100644 packages/meshbay-hub/tests/test_browser_idle_signout.py create mode 100644 packages/meshbay-hub/tests/test_session_lifetime.py diff --git a/CLAUDE.md b/CLAUDE.md index c8abbfa..164c541 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -794,7 +794,7 @@ here are kept only where they are a rule about *editing* the code. | Sign-in lockout | `meshbay_hub/login_throttle.py`, settings in `hub_settings.py` (`login.*`) | §7.7. **Every path that checks a passphrase calls `_take_login_attempt` first** — login, passphrase change, account deletion | | What answers without an account | `tests/test_unauthenticated_surface.py` — `PUBLIC` | §7.4. A new open route fails the suite until it is listed there with its reason | | Asset versioning | `meshbay_hub/api/webapp.py` — `_asset_version()` | the whole module graph is served under `/a//`, and the hash covers **every file under `static/`**, subdirectories included — nothing to register | -| Token lifetimes | `meshbay_hub/config.py` — `[jwt]` | 4 h access, 30 days refresh. **Production sets both in `~/.config/meshbay/hub.toml`** — changing the code default alone does nothing there | +| Token lifetimes | access: `meshbay_hub/config.py` — `[jwt]`; session: `hub_settings.py` — `session.*` | 4 h access token, from `hub.toml`. **The session is an admin setting in the database** (browser idle sign-out, refresh idle, maximum; `static/idle.js` measures the first). A `refresh_token_ttl` still in `hub.toml` is ignored with a warning | ### Browser / desktop UI (`meshbay_hub/static/`) diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index ca5ba8e..4cac3ac 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -1779,6 +1779,21 @@ Registration is gated by a CAPTCHA whenever one is configured — **unconditiona not only when some other field is absent, or the real client's ordinary request skips it. The desktop client renders the widget too. +**Sessions end three ways, all admin settings in hours** (`session.*`, §7.4): + +- **A browser signs itself out** after `browser_idle_hours` with no input and no + `