Skip to content

Open apps, including Chat, stay connected while Home runs and reconnect by themselves - #532

Merged
andersalm merged 9 commits into
developfrom
fix/home-frame-session-renewal
Nov 5, 2026
Merged

andersalm merged 9 commits into
developfrom
fix/home-frame-session-renewal

Conversation

@andersalm

Copy link
Copy Markdown
Contributor

Deepen notes

  1. Expert view: fix the cause for every open app, not just Chat, and prove that no security lifetime grows: a renewed frame token never outlives the person's session, and a stolen frame token alone cannot renew anything.
  2. Raw material: develop 6f1e2f1: gateway_home_token.rs:301 (12-hour launch token), room_service.rs:47 (12-hour room session), gateway_room.rs:1962-1994 (polling never renews it), chat-room-ui/src/lib.rs:4370-4404 (Chat stops polling after a 401 until Reconnect), home-shell-host.js (Home renews its own session but never open frames).
  3. Methods: reproduce with short-lived tokens and fault injection instead of waiting 12 hours; cap renewal at the parent grant; move frame associations instead of cancelling them; bounded exponential backoff with jitter.
  4. Independent check: tests that failed before and pass after (below); two Claude reviews via Cursor (round 1 FAIL with two blocking findings, round 2 PASS on 0c61f0b); full Linux remote check PASS on 0c61f0b (gate, test-elastos, test-capsules).

Problem

Anders, 4 Nov: Chat goes offline after about 12 hours and needs a page refresh, and should instead stay connected while Home runs and reconnect by itself after a network drop or restart. Two 12-hour expiries were never renewed for open windows (the app launch token and the configured room session), and Chat stopped polling for good after its first 401.

Change

  • Server (c7f1a7b): a renewed Home frame token expires at the earlier of the existing launch TTL and the current parent grant's expiry; renewal is refused for an expired, revoked or other principal's grant. No TTL constant changes.
  • Home (2ea2a4a, 0c61f0b): renews each open frame's launch authority before it expires, only while Home's own session is valid and only for the registered window, origin and target; moves the frame's associations (Library picker, clipboard, link prompts, Browser renewals, Wallet state) to the fresh token; per-target allowlist of renewal query keys; a picker id is honoured only for the frame that owns that picker.
  • Chat (4be4d4a, 9370643): renews its room session through the existing /session/start; one recovery loop with backoff capped at 30 s and jitter; shows Reconnecting and keeps history, draft and cursor; on 401 asks Home for fresh authority first; session-start errors stay typed and bounded (no raw server text reaches the UI).
  • Other apps (c58f7d6): Documents, People, Assistant, Library and Browser's adapter use the renewed token for later requests, including Documents' save.

Tests (fail before, pass after)

  • scripts/chat-online-behavior.test.mjs (runs in ./scripts/test-behaviour.sh): Community and Direct Chat resume without a click after frame expiry, 401 and room expiry; network and 503 recovery; a long outage bounds retries and resumes when back online; server error text never reaches the page.
  • scripts/home-frame-renewal.test.mjs 33, scripts/open-app-token-consumers.test.mjs 5; Rust: frame_renewal_is_bounded_by_launch_ttl_and_current_parent_grant, chat_online_configured_poll_does_not_renew_room_session.
  • Full behaviour suite 2,108 passed; server 3,229 passed (7 provider-fixture failures also fail on clean develop and pass in the Linux check, which builds the fixtures).

Not proven

A real Home kept open 12 hours, physical sleep, a real Runtime restart. #497 (the sign-in screen tearing down app windows) is separate.
Known small gap from review: a Library picker window loses its mode=attach query when its own authority renews; the selection itself still transfers.

Security review requested from Irzhy before this goes into a release (sign-in renewal).

CI fixtures (2d03c35, found by the push queue CI precheck): People and Library smokes serve the Home navigation module the apps now import; the configured Chat layout smoke no longer clicks Reconnect after choosing Community, because choosing it now recovers by itself.

Expose the issued launch expiry and bound projected frame tokens by the active parent grant and existing TTL. Preserve verifier-owned projection scope and sign malformed Wallet fixtures directly so the bridge's rejection checks still reach provider admission.

Verification: 142 focused server tests passed. The full server run had 3229 passed, 7 missing-provider-fixture failures also reproduced on clean develop, and 27 ignored. Server Clippy passed with -D warnings; workspace formatting passed.
Use Home's current authority to replace a registered frame token without replacing its document. Keep source, origin, target, request and principal checks through prepare and commit. Move picker ownership, Clipboard state, Browser renewals, external-link prompts and Wallet effect state to the fresh context.

Verification: focused Node suite passed 158 tests, including renewal and association transfer controls. Source gate passed 621 counted tests plus artifact, smoke and integrity checks.
A final whitespace check and the 20-test renewal file passed before commit.
Renew the room session before expiry and recover frame and room authority after temporary failures. Keep the cursor, history and drafts, show Reconnecting, and use jittered backoff capped at thirty seconds. Rebuild the committed Chat UI artifacts.

Verification: 95 Chat Rust tests and 10 browser fault tests passed, including Community and Direct recovery, expiry, connection loss, 503 responses and the long-outage retry bound. Chat Clippy passed with -D warnings; formatting and source/artifact matching passed.
Connect People, Documents, Assistant and Library to the shared renewal callback and Browser to its existing renewal adapter. Refresh request and Clipboard credentials while retaining the open document and draft. Add the user-facing Unreleased changelog entry.

Verification: all five token-consumer tests passed in the 158-test focused Node suite; Assistant regression tests and the source gate passed.

Final behavior suite: 2092 passed with zero failures. Source gate: 621 counted tests plus artifact, smoke and integrity checks passed. Installed 12-hour use, physical sleep and a real Runtime restart remain human acceptance gates.
Goal: keep server session-start details private during bootstrap, room renewal and Home recovery, while running the real Chat journeys with the behaviour gate inputs.

Return typed bootstrap failures from the session helper, classify authorization loss by variant, and render only bounded detail text. Add three call-site browser regressions that track secret-looking error bodies across rendered UI transitions. Load pinned Playwright Core from BROWSER_OPERATOR_PLAYWRIGHT_CORE and rebuild the committed Chat artifacts.

Verification: 95 Chat Rust tests and all-target Clippy with -D warnings passed through elastos-heavy. All 13 Chat browser tests passed with the pinned input and no checkout-local node_modules. The three privacy regressions failed against the prior WASM. Formatting, product-data checks and artifact parity passed.
Goal: each app renews only its own presentation selectors, and Library retains picker ownership with the requesting frame.

Allow only the keys each renewal consumer uses. Accept pickerRequestId only for the current Library frame and an active owned picker, then transfer that association without opening a new picker. Add rejection controls for arbitrary keys and foreign, retired or copied picker authority, plus valid selector controls.

Verification: the 33 renewal tests passed; eight regressions failed before this fix. The focused Node run passed 87 tests, including Chat reconnect, token consumers and external-link association coverage.
Serve the Home navigation client in People and Library performance fixtures. Let Community selection trigger recovery in the initial Direct-401 Chat journey, while retaining the one-launch, selected-conversation and same-document checks.

Verification: product-ui-source and product-ui-browser passed with pinned Playwright 1.59.1 Chromium. People timed out before the fixture fix and passed after; Library performance failed with the missing module and passed after. The full behaviour suite passed 2108 tests with zero failures or skips. Product-data checks, 105 data tests, both Rust formatting checks and git diff --check passed.
@andersalm

Copy link
Copy Markdown
Contributor Author

Independent source review: bounded PASS on 27b58156be010b1ff47dc54b224f5e1a5f1fdbf0, tree bb16216da1ab9e3800bcece416eb50c0c2e009c4, against PR base 27ea1032d1d0ad12f45a968f5d14fccd929d4af0. I found no new blocking source issue in parent-grant expiry, registered-window renewal, target/query ownership, association transfer or Chat recovery. Renewal and Chat implementation bytes match 0c61f0b40; the later PR changes affect three smoke fixtures.
Independent checks on the frozen source: home-frame-renewal.test.mjs 33 PASS, open-app-token-consumers.test.mjs 5 PASS, chat-room-reconnect.test.mjs 6 PASS; Chat source/artifact stamp and exact-candidate git diff --check PASS. These used small disposable source snapshots. Main's full Linux check and current 15/15 CI remain supplied owner evidence.
Irzhy owns the required security review before release. Installed long-session, physical sleep and real Runtime restart acceptance remain open, together with the recorded Library attach-mode query gap. This source result leaves those product and human gates with their owners. Complete Chat acceptance stays in #86.

@irzhywau

irzhywau commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Independent adversarial security review — Home/app frame session renewal

Scope: fix/home-frame-session-renewal head 27b58156be (tree bb16216d9a), base 27ea1032d. Source review at a clean checkout, two-model independent pass; no fresh build, installed, or long-session run. Installed acceptance stays with #86.

Verdict: PASS (bounded). No blocking issue. Both security invariants hold, and the remaining observations are tracked privately.

Security claims under test

  1. A renewed frame token never outlives the person's session.
  2. A stolen frame token alone cannot renew anything — it can neither mint a replacement token nor escalate to another app.

Verified controls

Control Result
Renewed frame tokens are clamped to the active parent grant at mint, and the grant lookup rejects revoked or expired sessions Confirmed
Verification independently requires the grant to be active on every use, alongside signature/identity, absolute expiry, bounded lifetime, and non-delegatable checks Confirmed
Only the Home token (same-origin, session-bound) mints a frame token; the returned expiry is re-derived from the minted token Confirmed
A room session starts only under the chat-room frame token Confirmed
Home renewal commit re-validates signed-in identity, window-source binding, target identity, and freshness before transferring associations Confirmed
Per-target query allowlist, length/format bounds, and picker ownership bound to the requesting frame Confirmed
Message routing binds message origin and window source to the registered frame Confirmed

One pre-existing session-lifetime behavior found during this review is recorded as S5–S7 in the private security advisory and does not affect this verdict.

Remaining gates (unchanged, owned elsewhere)

Installed 12-hour use, physical sleep, real Runtime restart, and the Library mode=attach query gap remain open in #86 and do not change this verdict.

@andersalm
andersalm merged commit 577bb01 into develop Nov 5, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants