2 For the platform team
mikebrandon edited this page 2026-09-04 11:53:49 +00:00

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

  1. Fail closed before the body is read. Every endpoint. An unidentified caller must not reach parsing, let alone storage.
  2. 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.
  3. 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.
  4. 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.
  5. 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/codes stands in for a web app with a signed-in person, and the live view logs against a placeholder actor. The administer scope 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.