sk_ token is
different: an opaque key whose hash the server looks up, with the tenant in its
prefix. A token that names another tenant is refused.
Developer testing
- An
sk_token carries your whole role unless you bind it to one agent (see Bound tokens below). - The lifetime is an install-wide setting.
0ornonemakes new tokens non-expiring. A token can ask for a shorter life (see below). - The plaintext is shown once. Only its hash is stored.
- A token minted this way cannot approve intakes, certify agents or activate obligations: those routes need an interactive sign-in.
Bound tokens
Agent binding, per-token lifetime and live revocation below arrived with the
2026-10-07 sengol release. Earlier releases ignore
agent_id and
expires_in_hours, mint an unbound token, and let an exchanged session run to
its exp. The Console form warns when the server did not apply either field.POST /v1/users/{user_id}/tokens also takes:
agent_id(up to 256 characters): binds the token to one agent. The exchanged session holdsaudit:writeonly, and its agent is the token’s, whateveragent_idthe client sends at exchange. The exchange returns403if the user’s role does not holdaudit:write.expires_in_hours(1 to 87600): a shorter life for this token. With an install-wide cap set, the token never outlives it.
POST /v1/audit/ingest, POST /v1/traces/otlp and
/v1/capture/*. Every other route returns 403. Audit ingest refuses with
403, before writing anything, a record that names another agent. Use a bound
token for the audit sink, not for the other SDK clients.
The Console mints one from Settings, Access, Users, with an optional agent id
and lifetime, and lists each token’s agent, expiry (UTC) and status.
Every session exchanged from an sk_ token ends when the token is revoked or
expires, or its user is disabled: the next request that reaches the revocation
check returns 401. A request the rate limiter stops first returns 429. An
exchanged session never outlives its token, and lasts at most one hour. A
session exchanged before an upgrade carries no token reference and runs to its
own expiry.
Admin
- The first admin is seeded from
SENGOL_BOOTSTRAP_ADMIN_EMAILat boot.grant_type=console_adminexchanges the install’s bootstrap secret (SENGOL_CONSOLE_ADMIN_SECRET) for a one-hour admin token. That token is not an interactive sign-in: it cannot approve intakes, certify agents or activate obligations. Use a password, SSO orsengol login --devicesession for those. - In
localmode an admin can mint ansk_token for any user the admin outranks (POST /v1/users/{user_id}/tokens). The token is that user’s: it carries that user’s role and subject, not the admin’s. The change is logged asapi_token.createwith the admin as actor.
CI/CD
CI exchanges the runner’s OIDC token (grant_type=github_oidc, or
ci_oidc for another issuer in SENGOL_CI_OIDC_ISSUERS) for a one-hour token.
The repository allowlist, the CI principal marker and the
403 on audit ingest
and OTLP below arrived with the 2026-10-07 sengol release. Earlier releases do
not apply them.- Allowlist. Only repositories named in
SENGOL_CI_OIDC_ALLOWED_REPOSITORIEScan exchange:owner/repo,owner/*or a numeric project id. An unset or empty list admits nobody. An entry without a prefix applies to the GitHub Actions issuer only. For aci_oidcissuer, prefix the entry with that issuer, as inhttps://issuer.example=group/project; without the prefix that issuer gets401. An issuer whose token has no repository claim is matched on the token’ssub: exact, case-sensitive, with noowner/*or numeric id. - Scope. The token carries
audit:writeand is marked as a CI principal. It is never an interactive sign-in, so it cannot approve, certify or activate. - Refused routes.
POST /v1/audit/ingestandPOST /v1/traces/otlpreturn403to a CI token. CI tokens still reach the routes that write their own records, such as gates. - Identity. The token’s
agent_idis the repository path (owner/repo), or, for aci_oidcissuer whose token has no repository claim, the token’s verifiedsub. It is not a registered agent id. No branch, environment or workflow restriction is applied; those claims are recorded in the workflow audit log only.
Production agent
Both give the same token:
audit:write only, bound to one agent, never
interactive. Revoking the registration secret stops its tokens.
The SDK sinks read
SENGOL_API_TOKEN, or do the CI exchange. They do not read
SENGOL_REGISTRATION_SECRET, and neither registration_secret nor
agent_oidc is wired into the SDK today. An agent running through the SDK
therefore holds a token you supply, typically an sk_ token in local mode.
Unless you bind it to the agent (see Bound tokens), it
carries that user’s whole role.grace_days (default 7, range 1 to 30) unless you
choose revoke_now or keep. Revoking or emergency-revoking a secret stops
tokens already issued from it on their next request. A bound IdP identity is
not tied to the token’s one-hour life.
Entra and other IdP sign-in
grant_type=oidc verifies the IdP token against the install’s SSO connection,
maps the token’s roles or groups to a Sengol role, and issues a one-hour token
marked interactive. The IdP’s amr and acr claims are copied into it.
- Step-up (
mfainamr) is checked only for CRITICAL-tier transitions, and only whenSENGOL_SOD_REQUIRE_STEP_UPis set totrue. - The grant does not check what kind of token it was given. Do not map an
app role or group to a Sengol role unless every holder is a person; an
Entra service principal carries app roles in
rolesand would be treated as a signed-in person. Useagent_oidcfor a non-human identity. - A synced directory account used as a service account arrives like any user and is indistinguishable from a person.
- A user disabled through SCIM cannot exchange a new token. A token already issued stays valid until it expires, up to one hour. A user with no SCIM record is stopped only when the IdP stops issuing tokens.