Previously POST returned only { id, name }, while GET returns
{ id, name, state, lastOutputAt }. iOS clients that share a Decodable
for both endpoints (e.g. SessionItem in pi-remote-ios) failed to decode
the POST response with 'data couldn't be read because it is missing'.
The new session always starts in 'idle' state with empty lastOutputAt.
Documented the new shape in the route header comment.
Problem: isAuthenticated() for WS upgrade only checked legacy single token.
iOS bearer token (from POST /pair → createToken()) was rejected → 403 on WS.
Fix:
- warmTokenCache(): pre-load all multi-tokens into a sync Set on startup
- validateBearerSync(): O(1) sync lookup against the cache
- createToken(): adds to cache immediately on creation
- isAuthenticated(): checks validateBearerSync() as third fallback
- POST /pair: consumes one-time pairingToken, creates named bearer token,
returns { bearerToken, sidecarId } per IC-3
- isAuthenticatedAsync(): checks legacy token + new multi-token store
- isAuthenticated(): extended with Bearer header support for WS upgrade
- Smoke still 12/12 green
Create the modular server/ layout from PHASE-1-sidecar.md §Architecture Sketch:
server/types.ts — shared WsClient/WsServer/RemoteServer interfaces
server/upgrade.ts — WS upgrade routing per path (LEGACY /ws)
server/server.ts — HTTP bootstrap, middleware, LEGACY HTML routes
server/routes/ — empty dir, placeholder for T-1.5/T-1.6/T-1.7
The original extensions/remote-control/server.ts is replaced with a
thin re-export shim so that index.ts continues to resolve
'./server.js' without changes.
All LEGACY code paths (manifest.json, icon.svg, /, /ws) are tagged
with // LEGACY: … comments. No behaviour changes. No new endpoints.
Pre-existing biome errors in auth.ts, config.ts, index.ts are
unchanged — NOT introduced by this commit.