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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions