Member Credentials
Member Credentials is the Metallo context that holds a member's offline consent for native Site work and serves one fresh access token per native action. Each Slurm job runs with the member's personal credentials; nothing is impersonated. See ADR-0035.
Aggregate
domain.consent.OfflineConsent is one login's offline grant. A member has at most
one active consent, and a second login replaces it: the incoming offline token
becomes the Run's token and the earlier consent moves to superseded and waits
for the issuer to confirm its revocation. The other terminal states are lost
(the issuer refused the stored refresh token), expired (its measured window
ended) and revoked (the issuer confirmed the revocation; the row keeps no
sealed value).
The record never contains the refresh token. It carries a sealed value bound to the consent identity, so a sealed value cannot open in another row. Only the owner adaptor's sealer can open it, and no token reaches a log, a command, an outcome or a reply.
Operations
The token route lives under the command scope it serves, /v1/site/commands/:
the path names the command, the body names its Attempt only, and the Site and
environment are the ones the bearer proves. A Site API can therefore scope the
whole command prefix without a third route family. It is the
Runtime's own commands grant that covers the route (DD-0059 §8), so no third
native scope exists; a psb_ bearer is accepted only with that scope and the
slurm environment, while a Kubernetes Runtime keeps its site-control
audience. The grant's Site must be the one this listener serves, the single setting
SITE_API_SITE_ID every native route reads (no fallback, and
SSH_ACCESS_SITE, when present, must name the same Site), and the command's
saved attemptId must match the request.
Every other case gets one refusal: another Site, another environment, an unknown
command, a command of another Attempt and an acknowledged command all answer
403 member_token_refused with the same body, so a caller cannot learn whether an
Attempt exists. A bearer outside the commands grant stops earlier with a plain
403 forbidden, which says nothing about any Attempt. A consent that is missing, lost, expired, superseded or revoked becomes
ConsentRefused with one fixed code. The Runtime records a terminal Attempt refusal for
member_consent_missing, member_consent_lost, member_consent_expired and
member_consent_revoked, after releasing repository access and cleaning its workspace.
Execution Core prevents automatic retries and includes a sign-in-again notice in the
Run's typed projection. Temporary owner and transport failures remain retryable.
Rotation happens under a per-member advisory lock and the row's FOR UPDATE. The
adaptor persists the new sealed value and generation before the caller uses the
access token, so a crash between the issuer's rotation and the commit leaves a
consent that is provably unusable rather than one that silently continues with a
stale token.
The refresh worker is what keeps an idle Run inside the issuer's idle window: without
it, a Run that performs no native action for longer than the idle timeout loses its
consent even though the 30-day maximum has not been reached (ADR-0035). It takes the
same per-member lock as a native action, so a refresh and an action never rotate the
same token twice; it keeps no access token; and it lists only an active consent whose
issuer-reported deadline is still ahead. Project execution authority comes from the
resource view's member_generations, which keeps a Party that left a Project as
inactive, so a member who left is never refreshed; when that evidence is unavailable
nothing is refreshed for that member and the next pass asks again. The worker runs in
the Site API, beside the routes. While the Run path is paused it has no such authority,
and with no Run to keep alive it only retries revocations.
Configuration
All six MEMBER_CREDENTIALS_* values are required together, and the listener
refuses to start if it cannot name its Site; without an intake key it serves only
its other routes.
Storage
member_credentials owns its PostgreSQL database and schema: one active row per
member (enforced by a partial unique index), the ended rows kept until the issuer
confirms revocation. It has no broker, no platform database and no Runtime access.
Page source
Source snapshot · Revision f928834c8.