For the platform team
Read docs/PLATFORM-INTEGRATION.md. It is the handoff and it
is the only document you need to implement the server side: the seven ports you write, the
invariants you must not break with the name of the test defending each, the five endpoints with
the idempotency rules that are easy to get wrong, a migration checklist, and what is deliberately
missing.
This page is the orientation around it, not a summary of it.
What is and is not yours
Yours: everything behind the network boundary. Storage, identity, the web app, retention administration, whatever the console eventually talks to.
Not yours: the client. It is complete on Windows and macOS, it is not a prototype of a server-side design, and the local pipeline is not something to reimplement centrally. In particular the gate must stay on the machine: the whole argument is that content is filtered before it can leave, which a server-side filter cannot provide no matter how good it is.
The shape of the integration
The rules live in @here/platform-adapter, as interfaces. apps/admin is a throwaway dev
implementation of them — a Next.js app with an in-memory store — so you can run the client
end to end today and replace the implementation without touching the rules.
The ports:
| Port | Job |
|---|---|
IngestSink |
accept batches of events |
DeletionSink |
accept and confirm deletions |
LiveBus |
stream events to a live view |
Authenticator |
identify a caller, fail closed |
PairingStore + TokenMinter |
turn a short-lived code into a device token |
AccessLog |
record every read, where the read happens |
Write Authenticator first. The integration document says which and why; every other port's
behaviour depends on identity being settled.
The five rules most likely to be got wrong
- Fail closed before the body is read. Every endpoint. An unidentified caller must not reach parsing, let alone storage.
- Ownership is checked against the credential, never the request body. A device may only touch its own records. A body claiming a device id is a claim, not an identity.
- Idempotency is on the batch id, on both sides. A replayed batch stores nothing twice. The client retries; that is not a bug to work around, it is the durability model.
- A deletion for records you never had is a success, not a 404. And a partial refusal must come back as a refusal, not a confirmation — the client stores those as distinct states because the person is shown a receipt, and a receipt that says "deleted" about a surviving copy is the worst possible output of this system.
- A read that cannot be logged must be refused. Not logged-best-effort. If the access log write fails, the read does not happen. The client mirrors your access log locally so the person keeps their own copy of who looked.
Deletion, because it is the part with teeth
Deleting locally propagates to frames, blobs, digests and the upload queue. If the record was
already uploaded, a deletion request is queued at higher priority than new records, so it
overtakes ordinary traffic, and it is retried until the server confirms. The client tracks
not_needed | pending | confirmed | refused and shows the person which.
That means your POST /v1/deletions is not a nice-to-have endpoint. It is load-bearing for a
promise already made to the person on their own screen.
Retention
-1. Nothing expires; data is kept until somebody deletes it, because it is training data as
well as verification data. contracts-lint fails if the registry and a manifest disagree about
this. Do not build an expiry job — build the deletion path well instead. See
Decisions not to relitigate.
What is not built, so you do not wait for it
- Operator identity. Devices authenticate; people do not.
POST /v1/pairing/codesstands in for a web app with a signed-in person, and the live view logs against a placeholder actor. Theadministerscope is enforced, but nothing issues it. This is probably the first thing you need. - A durable store on the server side. The dev one is in-memory.
- Deployment of any kind.
here-telemetry
How it works
Working on it
Taking it further
In the repo: README.md for the
invariants, HANDOFF.md for
current state.