OldScreen uses capability-based access instead of mandatory user accounts.
| Credential | Lifetime | Capability |
|---|---|---|
| Pair token / pair code | 10 minutes, one use | Claim an unclaimed screen |
| Display token | Until the screen is deleted or expires | Read screen state |
| Admin token | Until the screen is deleted or expires | Read, update and delete |
The pair token is embedded in the QR code. The shorter pair code exists only for manual entry. A successful claim invalidates both.
- A display creates a screen through
POST /api/rooms. - D1 stores the screen state and three independent capability tokens.
- A controller claims it with the temporary pairing credential.
- The controller writes validated state with its admin token.
- Displays read state with their display token using conditional requests.
The display polls every 2.2 seconds while visible, slows to 15 seconds in the background, and exponentially backs off after failures. Responses use ETags so unchanged state carries no JSON payload.
D1 stores structured screen state. Screens expire after 90 days without an administrative update or pairing refresh. Expired screens and stale rate-limit buckets are removed opportunistically.
Browser storage contains device-local credentials and the last successfully displayed state. It is not the authoritative copy of room data.
- Tokens are generated from
crypto.getRandomValues. - API responses are non-cacheable.
- State is allow-listed and size-limited; photo and iframe URLs must be HTTPS.
- Creation and claim endpoints are rate-limited by a one-way hash of the client address.
- The worker adds CSP, frame, MIME sniffing, referrer and permissions headers.
- Embedded webpages are sandboxed and never gain OldScreen origin privileges.
There is currently no account recovery. Losing every controller browser means the screen must be deleted by expiry and recreated. Optional account-backed recovery is intentionally deferred.