Skip to content

Web client: share-link token auth + reconnect / IndexedDB cache #693

Description

@KrisSimon

Context

Child of #687. The smallest viable security + offline-tolerance story for v1. Big enough to be a separate PR, small enough that a real auth provider is explicitly out of scope.

Tokens

  • One token per running room, generated by `aro room` on start. 24 random bytes, base64-url. Printed once.
  • The hub accepts a token via query string (`?token=…`) and via the `Sec-WebSocket-Protocol` header. The URL form is for sharing; the header form is what the browser client actually uses after first load (URL token is moved into `sessionStorage` immediately so the share URL works after a refresh too).
  • `--token READONLY` mints an additional read-only token. The hub flags the WebSocket connection so `file-edit` frames return `{"error": "read-only"}`.

Reconnect

  • Client tracks the last `runtime-event` `ts` and `file-changed` rev it received.
  • On socket close: exponential backoff (`0.5 → 1 → 2 → 4 → 8 → 8 …` capped at 8 s) up to a 2-minute total before giving up. Surfaces a banner: "Reconnecting…" then "Lost connection — refresh to retry."
  • On reconnect, send `hello` with the cached `lastSeenRev`. The hub replays everything newer than that from its in-memory rolling buffer (last 5000 events ≈ 10 min of activity on a normal run).

IndexedDB cache

  • Per-room namespace: `solaro-room-{room-id}`.
  • Stores: latest file contents keyed by path (so a refresh shows last-seen state instantly while the WebSocket reconnects), the last 1000 `runtime-event` payloads (so the pulse history doesn't blank on refresh), the user's preferred peer name + colour.
  • Eviction: on a successful `welcome` whose `project.rev` differs from the cached rev, evict file contents and runtime history for that room. The peer profile sticks.

Files

  • `Sources/ARORuntime/Room/RoomAuth.swift` (new) — token store + check.
  • `webclient/src/rooms/RoomClient.ts` — reconnect logic + IndexedDB integration.

Out of scope

  • OAuth / SSO. The user said v1 lives on a trusted network.
  • Token rotation. The room dies with the host process; new room = new token.
  • Per-file ACLs. Either you can edit or you can't, room-wide.

Acceptance

  • Sharing a read-only token blocks `file-edit` server-side and the client surfaces the rejection as a banner.
  • Cycling the laptop's wifi for 30 s and back: the client shows "Reconnecting…", recovers, and the editor doesn't lose unsaved local edits.
  • Hard refresh of the browser tab: file content + canvas appear instantly from IndexedDB before the socket finishes its first round-trip.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions