Service endpoint access
Authority routes
With serviceEndpoints, both faces also serve /service-endpoint-authority, and without it that
route answers 404. GET returns the receiver's account and space, the exact iss its service
access bearers carry, and the public Ed25519 keys GET /jwks publishes. POST takes a {request, proof}
envelope of at most 2 MiB. An IdP proof derives the owner, who needs a current interactive cli or web
row; register needs one, and so may activate. A service's renewal proof authorizes activate, renew
and status, and its access bearer authorizes ready and status; neither is owner authority. A typical
sequence: the service registers and gets approval-required; you bind its name with an operator
name-bind; the owner approves with decide; the service replays the same register and gets
registered with its witness and snapshot; it activates and gets a credential with an access bearer
of at most 60 s and a renewal proof of at most 24 h, neither outliving the native root or approval;
it connects to the broker with the bearer through the composed native service callout, binds its
responder, calls ready, and renews before the bearer expires. The auth provider's
serviceEndpointInfo and serviceEndpointAuthority hooks are the client, and accept a service proof
as well as the signed-in login.
Verified serving grant
The credential answer carries no store access and no serve grant, so a service turns it into the
process-local grant serveEndpoint consumes with authorizeGuestServeGrant({ accountPublicKey, result, registration, authenticity }): the parsed result, the service's own embedded closure, and
the pinned issuer's iss and published keys. The issuer and keys come from the registered
space's authority URL the service already pins, tenant path included: the auth provider's
serviceEndpointInfo answer carries them, and a service passes serveAuthenticityOf(info) as
authenticity; an authority that does not publish them yet makes that call refuse, so the
service does not serve on a bearer it cannot verify.
It verifies the access bearer's EdDSA signature under the published key its kid names and the
same closed claims the host's token validator checks, so a bearer the issuer did not sign for
this witness brands nothing; the closure is bound to the service's own embedded contract, not to
the bearer. It refuses
another account, a bearer from another issuer, space, subject or key, one that names another
witness or no serve duty, a closure set or artifact that is not the embedded one, and a closure
that does not read at its digest. authorizeTrustedServeSnapshot stays the host's.
Renewal source
For a serving connection, the auth provider's serviceEndpointRenewal({ store, dir, witness }) hook
returns that loop as a bearer function. It is an AuthProvider method, not a package export. Pass
its bearer as EndpointOptions.bearer and its sentinelCreds with it. The
first pull activates on the signed-in login and every later pull renews on the stored renewal proof.
If that proof has expired, as after a machine was off for more than a day, the next pull replays the
same activate request on the signed-in login, which mints again while the root and the approval stand.
It keeps one activate and one renew requestId per witness, written to store before the first send,
and replays them, with fresh material each time, for as long as the root and the approval allow.
Concurrent pulls share one request. credential() is that same pull answering the validated
credential result without its renewal proof, which never leaves the source, as a copy per caller;
it is what authorizeGuestServeGrant takes. bearer() and credential() share one pull, so the
two in flight together send one request under the same persisted ids, and an ended or closed
source refuses both the same way. It owns no timer. A transport failure, rate-limited, a missing
or rejected sign-in, or a store failure leaves it unavailable, and the next pull replays the same
request; any other denial from the space ends it, and later pulls send nothing. state() reports
expiries only, errors are fixed sentences, and close() aborts a pull in flight.
Recovery
Every effectful request is keyed by its requestId and body. Keep one requestId per operation and
witness, store it with the witness and the renewal material before you send it, and reuse it with
the same body after a restart: a replay is safe, a changed body under the same requestId is a
conflict, and one instance accepts at most 64 distinct requests, so fresh keys per refresh or
restart exhaust it. A register requestId stays bound to the body and proposal its first answer
admitted: after approval, replay that exact request, and after a refusal or expiry, propose again
under a new requestId. A 503 means the request may be bound: replay the same request, or read its
status with target: { kind: "operation", operation, requestId }; the selector names the operation
since this release, and the old { kind: "operation", requestId } no longer parses. An owner reads
status only while it holds an interactive row, and a service proof reads only its own instance.
registered and ready in a status mean only that the request's registration is current. A
pre-activation re-registration sends expected: { state: "registered", witness } and yields a new
witness; a register that another request overtakes before its barrier freezes answers conflict,
so read the instance and expect its current witness. A register whose gate moved under it without a
new registration, such as a freeze another barrier then aborted, also answers conflict with the
witness unchanged: if the instance still shows the witness you sent, replay the same request. A
register held inside the native barrier, or while another invocation of the same request is still
running, answers 503; replay the same request. Once a service has activated, its registration
can no longer change. Commands must be ephemeral and declare
no governed trait.