Security model
Who may call what
- Three roles, each including the ones before it:
viewer<operator<admin. Every route of/api/v1names its least role; a route that names none needsviewerfor aGETandoperatorfor anything else. Too low a role is403 forbidden_role. The full table is Roles and permissions. - Credentials, tried in order: a bearer token whose SHA-256 is in
JOOBQUI_API_TOKENS(with an optional:viewer,:operatoror:admin; no role meansoperator), a token an admin created in the console (kept only as a hash and looked up on every request, so revoking it works at once), then the console's session cookie. - Browser sessions are encrypted, signed cookies that stop working
JOOBQUI_SESSION_TTL_MINUTESafter sign-in (60 by default). A cookie must sendX-Requested-With: joobquion every state-changing request; bearer tokens are exempt. Signing out revokes the session on the server. - The SDK gateway (
/api/v1/sdk/*) takes one shared token,JOOBQUI_SDK_TOKEN, compared in constant time. It acts as anoperatoron the gateway routes only. Unset, the gateway answers503 sdk_token_not_configured; a wrong token gets401 sdk_token_required. The gateway exposes no management operation (pause, resume, re-drive, cancel, terminate, concurrency): those are console operations. - Two-factor authentication (TOTP with recovery codes) can be required per role (
JOOBQUI_MFA_REQUIRED_ROLES), and OpenID Connect single sign-on maps the provider's groups to roles (JOOBQUI_OIDC_ROLE_MAP). See Accounts, SSO, 2FA, roles.
What is recorded
Every state-changing call is written to the audit log as METHOD /path, with a better row (token.create, user.role, mfa.reset, ...) where the route records one. Changes to who has access or what protects it are critical: an intent row is written first, and the call is refused with 503 when it cannot be. Enqueues, starts, schedule and blueprint registrations through the SDK gateway are audited too (sdk.job.enqueue, sdk.workflow.start, ...). See Auditing.
What is hidden
- Secret-looking values are masked in every answer: job payloads, schedule arguments, stream events, workflow inputs, outputs and query results. A key is secret-looking when its name looks like a credential (passwords, secrets, tokens, keys, authorization headers, cookies and the like). The match is generous, so some harmless names are masked too.
- Sealed job fields (
encrypt_fieldsin the SDKs,encrypted_propertyin Crystal) are encrypted withJOOBQUI_PAYLOAD_KEYSbefore the job is stored; Redis never holds them in plain text. See Payload encryption. - Secrets at rest: notification-channel URLs and keys and two-factor secrets are sealed (AES-256-GCM) with
JOOBQUI_SECRETS_KEY; API tokens and recovery codes are stored only as hashes; passwords as bcrypt hashes. - A secret never appears in a log line, an audit row, an error message or an answer, except the one answer that hands it out once (a new API token).