Skip to main content
This page states what the code does today. Where a credential does not exist in your auth mode, or an obvious expectation is not met, it says so.
Auth mode decides which credentials exist. sk_ API tokens exist only in local auth mode. Minting one in any other mode returns 503. An install that signs people in through an IdP (oidc) has no sk_ tokens, so the developer and production-agent steps below that use one do not apply to it. This guide covers local and oidc installs. SAML sign-in is not covered here.
Session tokens are signed JWTs that name this install’s tenant. An 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. 0 or none makes 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 holds audit:write only, and its agent is the token’s, whatever agent_id the client sends at exchange. The exchange returns 403 if the user’s role does not hold audit:write.
  • expires_in_hours (1 to 87600): a shorter life for this token. With an install-wide cap set, the token never outlives it.
A bound session reaches only 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_EMAIL at boot. grant_type=console_admin exchanges 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 or sengol login --device session for those.
  • In local mode an admin can mint an sk_ 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 as api_token.create with 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_REPOSITORIES can 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 a ci_oidc issuer, prefix the entry with that issuer, as in https://issuer.example=group/project; without the prefix that issuer gets 401. An issuer whose token has no repository claim is matched on the token’s sub: exact, case-sensitive, with no owner/* or numeric id.
  • Scope. The token carries audit:write and 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/ingest and POST /v1/traces/otlp return 403 to a CI token. CI tokens still reach the routes that write their own records, such as gates.
  • Identity. The token’s agent_id is the repository path (owner/repo), or, for a ci_oidc issuer whose token has no repository claim, the token’s verified sub. 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.
The one-hour figure is the issued token’s life, not the source credential’s. A registration secret is revocable and can carry an expiry. Rotating it keeps the old secrets working for 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 (mfa in amr) is checked only for CRITICAL-tier transitions, and only when SENGOL_SOD_REQUIRE_STEP_UP is set to true.
  • 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 roles and would be treated as a signed-in person. Use agent_oidc for 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.