Three uuids on one arrival, three different answers
Asked of the row the feed actually returns. site_id had a reference all along and the feed was not sending it. A client could read the shop's NAME off an arrival and still had no way to ask for that shop except by uuid - the exact gap the reference scheme exists to close. site_slug now travels with it. visit_id stays a uuid and needs no reference: no route takes it, it is a key a client de-duplicates on because delivery is at-least-once, and nobody says a visit id out loud. The uuid in a face URL must STAY random. visit_faces.id is gen_random_uuid() and a derived or sequential one would let somebody walk a shop's customers by date - the same reason bucket keys are random rather than derived from the event id. A readable identifier is right for a customer and wrong for the thing that points at their photograph. And seq is now json:"-". visits.seq is a plain bigserial, so it counts every visit on the PLATFORM, and shipping it put the total footfall of every customer we have on every row of every tenant's feed - the same German-tank estimate that decided visitors.number had to be per client. It was a convenience for "have I fallen behind", nothing ever read it, and the cursor answers that without disclosing a number. The SSE event id was never the raw value; it has always been the opaque cursor. The one test that broke was reading seq back off the wire to assert the cursor pointed at the last row of a burst. It asserts against the seeded position now: the property is unchanged, and the test can no longer see what a client cannot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HViLj9gYNRtSr7YVZmW5sn
This commit is contained in:
17
API.md
17
API.md
@@ -220,9 +220,10 @@ in. Reactivating restores the account but not their old sessions.
|
||||
```json
|
||||
{
|
||||
"arrivals": [{
|
||||
"visit_id": "…", "seq": 412,
|
||||
"visit_id": "…",
|
||||
"occurred_at": "2026-09-05T06:01:45Z",
|
||||
"site_id": "…", "site": "TeNext Chennai", "camera_id": "Office1",
|
||||
"site_id": "…", "site": "TeNext Chennai", "site_slug": "chennai",
|
||||
"camera_id": "Office1",
|
||||
"visitor_id": "…", "visitor_ref": "V-42", "label": "Priya",
|
||||
"is_new_visitor": false, "similarity": 0.71, "quality": 0.66,
|
||||
"attributes": { "gender": "Male", "age": 32, "emotion": "neutral" },
|
||||
@@ -243,6 +244,18 @@ again without one.
|
||||
|
||||
An empty poll returns your own cursor back, not an empty string.
|
||||
|
||||
**There is no `seq` on the wire.** It existed as a convenience for "have I
|
||||
fallen behind"; `visits.seq` is a plain bigserial, so it counted every visit on
|
||||
the *platform* and put the total footfall of every customer we have on every row
|
||||
of every tenant's feed. The cursor — opaque and version-prefixed — is the
|
||||
supported way to know your position, and the only one you need.
|
||||
|
||||
Of the three ids on an arrival, only one of them is a reference you would type:
|
||||
`site_slug`. `visit_id` addresses no route — it is a key for de-duplicating
|
||||
rows, since delivery is at-least-once. And the uuid inside an image URL is
|
||||
**deliberately random**: a derived or sequential one would let somebody
|
||||
enumerate a shop's customers by date.
|
||||
|
||||
### `GET /api/visits/stream` — server-sent events
|
||||
|
||||
The same rows, pushed. Send `Authorization` (so `EventSource` will not do —
|
||||
|
||||
Reference in New Issue
Block a user