Capabilities
Machine identity
Service accounts with rotating secrets, snt_ API keys capped by the owner's live permissions, and workload identity federation that swaps GitHub Actions or Kubernetes OIDC tokens for Sentinel tokens — secretless CI/CD.
Machines outnumber humans in most production systems, and their credentials outlive most laptops. Sentinel gives machine identity the same first-class treatment as human identity: three principal shapes, one authorization engine, and a way to stop shipping long-lived secrets to CI at all.
Service accounts
Client-credentials principals (ServiceAccount): a key, a display name, an optional
organization scope, and a hashed secret (sns_… prefix) with rotation and an overlap
window — the previous secret keeps verifying until its expiry so a fleet redeploy
never races a credential rollout. Managed by MachineAuthService
(CreateServiceAccountAsync, RotateServiceAccountSecretAsync,
VerifyServiceAccountSecretAsync), and consumable directly through the OIDC
client_credentials grant — a service account is an OAuth client where that’s
convenient. Rejected secrets emit service_account.secret_rejected; you find out about
brute-forcing, not just about lockouts.
API keys
API keys are Sentinel’s personal-access-token shape, and they inherit the two best ideas from the Node-era design:
- Effective permissions = owner’s current snapshot ∩ key scopes, denies preserved, recomputed at use time. A key is never more powerful than its owner is right now. Demote the owner and every key they minted shrinks in the same instant; a deny on the owner rides through the intersection untouched.
- The credential is the subject; the human is the attribution. The request context
separates
SubjectId(the key, for authorization) fromOwnerUserId(the person, for audit). “Which credential did this?” and “who answers for it?” get different, correct answers.
Mechanically: snt_ + 43 base64url chars (256 bits of entropy), SHA-256 at rest,
plaintext shown exactly once at creation, a 12-char display prefix for recognition in
lists. Presentation is just a header — Authorization: Bearer snt_… or a bare
Authorization: snt_… — handled by the same SentinelAuthenticationHandler that does
JWTs and cookies; the resulting SentinelPrincipal has Kind = ApiKey.
Management HTTP endpoints for keys and service accounts are a later deliverable this wave — today keys are minted via
MachineAuthServicefrom your own admin surface, and the HTTP tests exercise exactly that path.
Workload identity federation
The best machine secret is none. Workload federation exchanges the OIDC tokens that platforms already issue — GitHub Actions job tokens, Kubernetes service-account tokens, cloud managed identities — for short-lived Sentinel access tokens:
POST /oidc/workload/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJ… # the external platform's OIDC token
&subject_token_type=urn:ietf:params:oauth:token-type:jwt
&audience=example-api
A trust configuration decides who may exchange: issuer, JWKS (or discovery),
audience, a subject pattern with a single-* wildcard, and additional claim rules.
GitHub Actions, for example:
workloadTrusts:
- serviceAccount: ci-deployer
issuer: https://token.actions.githubusercontent.com
audience: sentinel
subjectPattern: "repo:nuvoralabs/*"
claimRules:
- claim: ref
value: refs/heads/main
The exchanged token is minted for the trust’s service account, carries a wtrust
claim naming the trust that authorized it, and comes with no refresh token — workloads
re-exchange, they don’t hold long-lived credentials. Denials return a uniform error to
the caller while the audit event (workload.exchange_denied) records the precise
reason: unknown_issuer, subject_mismatch, claim_mismatch:ref, and friends. The
HTTP test Github_style_token_exchanges_for_a_working_sentinel_access_token runs this
loop end to end.
Result: CI/CD with zero stored Sentinel secrets — revoke a trust, and a compromised pipeline has nothing.
The system principal
Background jobs get a first-class system principal type for attribution instead of
borrowing a human’s identity or running as an anonymous nobody — audit entries record
ActorKind (User, ServiceAccount, ApiKey, System, BreakGlass) so “who did
this?” always has an answer, even at 3 a.m. from a queue worker.
Learn by building
Article 007 — Workload identity federation configures a GitHub-style trust and exchanges a real signed token against a test host. For the audit trail these principals leave, see operations.
Learn by building
The tutorials for this area, in order — each with a runnable sample.