Guard / foldkit-guard

Who is asking

Authentication decides who is asking; guards only read it. Give the handlers G.Principal instead of a principal, and each call reads it, so one handler set answers every request, and whatever authenticates a request provides it:

ts
const handlers = RemoteServer.handlers(backend.server, G.Principal)

// per request: `body` is what it sent, and authentication resolved `principal`
RemoteServer.answer(handlers, body).pipe(Effect.provideService(G.Principal, principal))

A live hub shares a re-read between subscribers with one principal. Two requests by one person each decode a principal of their own, so give the hub Policy.keyOf, which names a principal by every field, as JSON keeps it (a set, a bigint, a date included; a visitor too), and they share one:

ts
const hub = yield* RemoteServer.liveHub([...backend.server.entities.values()], {
  key: Policy.keyOf,
})

Two principals with one key are read as the first of them, so a key that named only the person (G.key) would read a request made after their projects changed as an older request's claims; Policy.keyOf changes with any field. On the client, a cache snapshot is scoped by the person (RemotePersistence's scope: G.key(user)), and a sign-in or sign-out is Remote.forget (forget on a domain made with Remote.make), since every fact the cache holds was read as someone.