Console and authentication¶
Operator surface (/console/*), permission catalog and roles, system settings, and the two authentication modes. For why openKMS splits identity, operations, and data access, see Security design. For the data security model (RBAC vs resource ACL, sharing, inheritance), see Data security.
Console (admin)¶
- Entry: header user menu Console (when outside console routes) opens
/consolefor users with theadminrole, permissionall, orconsole:access. Inside/console/*, the header shows Exit Console back to home (/). The console shell and route gate still usecanAccessPathagainstfrontend_route_patternsfromGET /api/auth/permission-catalogfor individual pages (plus feature toggles where applicable). - Console overview (
/console): Introduces console sidebar tools only—system health, permissions, data security, data sources (each tagged by layer —dataorontology), Kubernetes clusters, object storage, users & feature toggles, settings; cards link whencanAccessPathallows; quick links to Permissions and Access groups when those permissions apply; optional nudge when the catalog still has onlyall(untilopenkms_permissions_onboarding_dismissedis set). - System health (
/console/health): requiresconsole:access(or admin).GET /api/admin/health-statusprobes core dependencies: API (implicit), PostgreSQL (SELECT 1), object storage (S3/MinIOhead_bucketwhen configured; otherwise skipped), background jobs (Procrastinateprocrastinate_jobstable present), Ontology Function Service (GET {OPENKMS_ONTOLOGY_FUNCTION_SERVICE_URL}/health), and Langfuse whenLANGFUSE_BASE_URLis set on the backend:GET {base}/api/public/health(skipped if unset; skipped with a short note ifLANGFUSE_HEALTHCHECKis false). Tracing credentials on the server (LANGFUSE_SECRET_KEY/LANGFUSE_PUBLIC_KEY) are not sent to the browser; the detail text only states whether they appear configured. When the caller also hasconsole:data_sources, each registered data source is connection-tested (PostgreSQL / Neo4j; same logic asPOST /api/data-sources/{id}/test). The page shows overall status, per-component latency, last-checked time, and a Refresh control in the page header (top right). Route/API patterns forconsole:accessinclude/console/healthandGET /api/admin/health-status(migrationr8s9t0u1v2w3refreshes defaults). PublicGET /health(no auth) remains a minimal liveness probe for load balancers and smoke tests—not the Console page. - Permission management (
/console/permission-management): Permission catalog is stored insecurity_permissions; the page loads rows fromGET /api/admin/security-permissions(includesidfor edit/delete). Under Roles, All selects catalog-only mode (add/edit/delete permission rows). Choosing a named role shows checkboxes to draft which keys that role receives; Save role permissions callsPUT /api/admin/security-roles/{id}/permissionsonce—no auto-save on each toggle. Switching roles with unsaved changes prompts to discard. Migrations seedallwhen the catalog table is empty and backfill default pattern rows for every hinted operation key (a2b3c4d5e6f7); admins may add keys via Add permission, Add missing suggested keys (fromoperation_key_hintsonGET /api/admin/permission-reference), orPOST /api/admin/security-permissions, using the in-page Route & API reference (and Operation keys tab) for path patterns. Roles may only assign keys that exist insecurity_permissions. The built-inallrow cannot be edited or deleted. Migration seeds the admin role withall; member is created on first non-admin local sign-in (also starts withall). You cannot removeallfrom a role that still has onlyallin one step—add another permission, save, then removeall. Local:user_security_rolessynced fromis_admin. OIDC: JWTrealm_access.rolesmatchsecurity_roles.name; realmadminbypasses permission checks. - Data security (
/console/data-security/issues,/console/data-security/groups,/console/data-security/groups/:groupId): requiresconsole:groups. Issues lists sharing misconfiguration with inline fix; Access groups uses a master–detail layout (group list, members tab, shared-access tab). Legacy/groups/:id/membersredirects to/groups/:id. Per-resource sharing is documented in Data security. - Data Sources (
/console/data-sources):console:data_sources(or admin). Kubernetes clusters (/console/kubernetes,/console/kubernetes/{id}):console:kubernetes— register existing clusters with encrypted kubeconfig, test connectivity, and browse namespaces / Deployments / Pods (read-only); see Kubernetes clusters. Object storage (/console/storage):console:storage— paginated bucket browser (S3list_objects_v2); create folder placeholders; multi-select move prefixes/objects; metadata only (no presigned download). Connectors (/connectors):connectors:read/connectors:write— see Connectors. Datasets (/datasets):data:writeto register/ingest and manage;data:readto view (filtered by dataset ACL);data:editto edit row data in editable datasets. The data layer (datasets/data-model) is gated only bydata:*; the Data Insights has its owninsights:read/insights:write(a story is not the same grant as the data behind it — and a dataset-backed finding is still checked against that dataset's ACL), separate fromontology:*andconsole:*. Object types / link types (/ontology-manager/object-types,/ontology-manager/link-types):console:object_types/console:link_typesorontology:read/ontology:writeas applicable; System Settings, Users & Roles, Feature Toggles remainconsole:*(or admin). - Users & Roles (
/console/users): requiresconsole:users. Local auth: list users, toggleis_admin(syncs security role links), delete/add users. OIDC auth: read-only list ofoidc_identities(users who have signed in); roles remain in the IdP. - System settings (
/console/settings):console:settings(or admin).GET /api/system/settings/PUT /api/system/settingsload and persistsystem_settings(Postgres singleton row):system_name,default_timezone,api_base_url_note(optional note only; SPA API URL remains build-time). APUTwhose trimmedsystem_namewould be empty is stored asopenKMS.GET /api/public/systemreturns{ "system_name" }without authentication (strict middleware allowlist); the value is the trimmed DB field and may be"". The sidebar title stays blank until that response arrives, then showsopenKMSwhen the name is empty or whitespace (otherwise the returned name); on fetch failure it showsopenKMS. After saves,notifySystemSettingsUpdatedtriggers the same fetch (custom event). Migration seeds rowid=1and attachesGET/PUTpatterns toconsole:settingsfor strict API enforcement. - Feature toggles:
evaluations(default off),connectors(default on),agents(default on), andmedia(default off) – persisted in PostgreSQL (feature_togglestable), shared across all users/devices. Whenagentsis off, the SPA hides/agentsand/projects/*, and/api/projects/*plus/api/user/git-credentialsreturn 404. Whenmediais off, the SPA hides/media/*and/api/media*plus/api/media-channels*return 404. GET /api/feature-toggles(authenticated) returns current toggle statePUT /api/feature-togglesrequiresconsole:feature_toggles(or JWT admin)
Permission catalog (canonical keys)¶
Defined in backend/app/services/permissions/permission_catalog.py; seeded into security_permissions by migration. Roles may only assign keys that exist in the catalog.
| Family | Keys |
|---|---|
| Console | console:access, console:users, console:groups, console:permissions, console:settings, console:feature_toggles, console:data_sources, console:kubernetes, console:storage, console:datasets, console:object_types, console:link_types |
| Connectors | connectors:read, connectors:write |
| Documents | documents:read, documents:write |
| Document channels | channels:read, channels:write |
| Articles | articles:read, articles:write |
| Knowledge bases | knowledge_bases:read, knowledge_bases:write |
| Evaluation | evaluation:read, evaluation:write |
| Wiki | wikis:read, wikis:write |
| Ontology | ontology:read, ontology:write |
| Data layer | data:read, data:write, data:edit (Datasets / Data Model apps; data:edit = edit dataset row data) |
| Media | media:read (browse the library), media:write (upload, edit, generate) |
| Data Insights | insights:read (open the app, read findings, re-run one), insights:write (create/update/delete stories) — plus the bound dataset's ACL |
| Knowledge map | knowledge_map:read, knowledge_map:write |
| Agents | projects:read, projects:write (projects, skills registry, agent chat; feature toggle agents) |
| Catch-all | all (built-in admin key; cannot be edited or deleted) |
Authentication¶
- OIDC mode (default): any OIDC IdP – Authorization Code + PKCE in browser (
oidc-client-ts); RP-initiated logout when the IdP exposesend_session_endpoint. On login,oidc_identitiesstoressub→preferred_username/ email from the access token (callback,POST /sync-session,GET /api/auth/me) for sharing UI and ACL alias resolution—no IdP Admin API and no personal API key required. - Local mode (
OPENKMS_AUTH_MODE=local): sign-up whenOPENKMS_ALLOW_SIGNUP(exposed asallow_signuponGET /api/auth/public-config); sign-in with username or email + password; users stored in PostgreSQL; HS256 JWT + session cookie; no built-in admin password (first signup only becomes admin). The UI usespublic-configso it stays aligned with the server even ifVITE_AUTH_MODEdiffers. - openkms-cli: OIDC client credentials (Bearer) or, in local mode, HTTP Basic (
OPENKMS_CLI_BASIC_*) - Worker / scheduler (heartbeats on
/internal-api/process-heartbeat): separateOPENKMS_WORKER_OIDC_*orOPENKMS_WORKER_BASIC_*on the backend and those processes (client id must be inOPENKMS_INTERNAL_SERVICE_CLIENT_IDSfor OIDC) - Profile (
/profile): authenticated users see display name, email (if present), administrator yes/no, realm roles, and resolved permissions (local users: DB keys such asallor granularconsole:*; OIDC IdP admins receive the full catalog); data fromGET /api/auth/me. Linked from the header user menu. - Settings (
/settings): API keys — create, list, and revoke personal API keys for assistants and scripts (POST/GET/DELETE /api/auth/api-keys); the full secret is shown only once at creation. Linked from the header user menu (Settings). - OIDC + API traffic: the token provider used by
getAuthHeaders()returns the access token only (nosetUser/ noPOST /sync-session+GET /api/auth/meon every token read). Session sync and profile refresh run from OIDC init andUserManageruser events. The SPA permission-catalog effect depends on a stable username / roles / permissions key so equivalentuserobjects do not refetch in a loop. - Idle / expired session on API calls:
authAwareFetchtreats structured401auth failures (see architecture Invalid JWT on API calls) as recoverable: one silentsigninSilent+sync-session(OIDC) or cookie/mecheck (local), then a single retry with refreshed headers; if that still fails, the app clears session, shows a short toast, and sends the user to sign-in again (local/login, OIDC interactive redirect). - Protected routes: under
MainLayout, all except home (/) require auth;/loginand/signupare separate routes. Unauthenticated users on/see the static marketing home; on any other path they see "Authentication Required". Authenticated users without JWTadmin/allmust match the union offrontend_route_patternsfromGET /api/auth/permission-catalogfor their keys (paths/and/profileare always allowed); otherwise "Access denied" with a link home. GET /api/auth/public-config(unauthenticated):auth_modeandallow_signuponly—no secrets—so the SPA matches deployed auth mode.- Backend accepts
Authorization: Bearer, session cookie (afterPOST /sync-sessionin the browser), and in local modeAuthorization: Basicfor CLI (mintedlocal-cliservice JWT). - Strict API patterns (optional):
OPENKMS_ENFORCE_PERMISSION_PATTERNS_STRICT— whentrue, authenticated/api/*must matchsecurity_permissionsbackend_api_patternsunless bypassed (all, realmadmin,local-cli, or allowlisted paths). See Configuration.
Obtaining an API token¶
Docs examples use Authorization: Bearer $TOKEN. How you get $TOKEN depends on OPENKMS_AUTH_MODE:
- Local mode —
POST /api/auth/loginwith JSON{ "login", "password" }returnsaccess_token(HS256). Prefer env vars +jqover embedding the password in the shell:
TOKEN=$(curl -sS -X POST "${API%/}/api/auth/login" \
-H 'Content-Type: application/json' \
-d "$(jq -n --arg login "$OPENKMS_LOGIN" --arg password "$OPENKMS_LOGIN_PASSWORD" \
'{login:$login,password:$password}')" \
| jq -er '.access_token // empty')
Default lifetime: OPENKMS_LOCAL_JWT_EXP_HOURS (168 hours). Long-lived tokens are convenient for dev scripts but widen blast radius if leaked.
-
OIDC mode — use the IdP user access token as
Authorization: Bearer …(verified via JWKS). Operation permissions for non-admin users come fromrealm_access.rolesmatched tosecurity_roles.name—the token must carry those roles./internal-api/models/...accepts onlysub=local-clior OIDC clients inOPENKMS_INTERNAL_SERVICE_CLIENT_IDS; human tokens and personal API keys get 403. -
CLI (local mode) —
OPENKMS_CLI_BASIC_USER/OPENKMS_CLI_BASIC_PASSWORD→Authorization: Basic …→ internallocal-cliJWT. Dev/trusted networks only. -
Worker / scheduler —
OPENKMS_WORKER_BASIC_*(local) orOPENKMS_WORKER_OIDC_*(OIDC client credentials); same rules as CLI for/internal-api(local-clior allowlistedazp). -
Personal API keys — Settings →
POST /api/auth/api-keys; plaintextokms.{uuid}.{secret}shown once. Local: permissions followuser_security_roles. OIDC: roles are snapshotted at creation—recreate the key after IdP role changes.
GET /api/auth/me confirms the token and lists resolved permission keys. Hardening themes: Tech debt — API tokens.
Credentials (do not commit)¶
| Variable | Purpose |
|---|---|
OPENKMS_OIDC_CLIENT_SECRET |
OIDC confidential client |
OPENKMS_SECRET_KEY |
Session cookie + local JWT signing |
OPENKMS_CLI_BASIC_PASSWORD |
CLI Basic secret (local mode) |
OPENKMS_WORKER_BASIC_PASSWORD / OPENKMS_WORKER_OIDC_CLIENT_SECRET |
Worker + scheduler internal API auth |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY |
S3/MinIO — env only, never CLI args |
OPENKMS_DATABASE_PASSWORD |
PostgreSQL |
Full list: Configuration. Use .env.example as a template; keep .env out of git.
Production checklist¶
- Strong
OPENKMS_SECRET_KEY(32+ random bytes). - HTTPS for frontend and backend; OIDC redirect URIs locked down;
OPENKMS_ALLOW_SIGNUP=falsein local mode if registration should be closed. - Review IdP realm roles vs
security_roles.name; prefer granular catalog keys over permanentall. - Restrict database and object storage to trusted networks; keep the VLM URL internal.
- Keep dependencies updated (
pip install -U,npm audit).
Static home (landing page)¶
- Public landing page for non-authorized users (
HomeStaticLanding, i18nlanding) - Pain points: knowledge scattered; evidence can’t travel with the story; manual parsing work
- Benefits: centralized knowledge hub; grounded answers and domain data; research that becomes a multi-slide analysis briefing
- Functionalities sections: document management, articles, knowledge bases (including semantic search when pgvector is configured), ontology & graph (datasets, object/link types, optional Neo4j), agents & analysis (installable skill, cross-surface research, interactive analysis presentations), pipelines & automation (jobs, per-channel pipelines, model linkage)