1
0

feat: add authenticated realtime presence

This commit is contained in:
2026-08-11 20:22:12 -07:00
parent 16dc85a6f8
commit 92ebf8abb5
20 changed files with 3746 additions and 1 deletions
+27
View File
@@ -31,6 +31,10 @@ without breaking the typecheck, which is the wrong order to find out.
| `GET /api/v1/markers` | — | `MarkersBody` | **private; 401 unless signed in** — public empty body when `TERA_MARKERS_SOURCE=none` |
| `GET /api/v1/offices/:id` | — | `OfficeDoc` | public offices only |
| `GET /api/v1/offices/:id/presence` | — | `PresenceBody` | **private; 401 unless signed in** — never publicly cached |
| `POST /api/v1/realtime/join` | — | strict `JoinRequest` or `ResumeRequest` | **private; never cached** |
| `POST /api/v1/realtime/events` | — | fetch-streamed SSE, opened with a strict `ResumeRequest` | **private; never cached** |
| `POST /api/v1/realtime/pose` | — | one owned `EntityPoseSnapshot` | **private; never cached** |
| `POST /api/v1/realtime/leave` | — | session id + opaque resume token | **private; never cached** |
Every body is declared once, in `src/server/wire.ts` in the **root** package —
type-only, so it compiles to nothing and both the browser build and this service
@@ -48,6 +52,29 @@ reply before any route runs, and a route opts in explicitly. A request that
arrived with an `Authorization` header or a cookie never gets a public policy,
whatever the route asked for.
### Realtime sessions
Hosted presence is an optional, in-memory adapter. It stores no pose history on
disk and disappears cleanly on restart; the static city and office continue to
work when it is absent. Joining requires the deployment's existing auth, and a
session owns exactly one opaque actor plus a bounded list of interest cells.
The server validates entity ownership, monotonic sequence/time, cell membership,
speed, climb and turn rate before broadcasting a pose only to the same cell.
The event stream is deliberately a **POST consumed with `fetch()`**, not an
`EventSource` URL. Session/resume credentials stay in the request body and
therefore out of browser history, referrers, Caddy access logs and copied URLs.
Tokens are random, stored only as SHA-256 hashes, rotated on resume, and bounded
by absolute TTL and disconnect grace. Reconnect, room capacity and pose rate
are capped. Authentication subjects remain server-only; peers see page-scoped
opaque entity ids and no profile name, face texture, email, or media locator.
California, city, office, floor and room cells are distinct coordinate and
authorization boundaries. Disk-backed office packs are checked before a floor
or room may be joined. The three public demo offices bundled into this repo may
use their office envelope without a duplicate server pack; that exception does
not invent floor/room access and does not apply to arbitrary tenant ids.
## Regions
**A caller's coordinate is never forwarded upstream. It only selects among the