feat: add authenticated realtime presence
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user