Files
ThothII/docs/plans/2026-08-20-psd-server-project-b-authentik.md
T

19 KiB

PSD Server Project B Authentik Integration Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Convert the accepted Project A installation to public OIDC mode, store owned work sessions in the existing Supabase database's thoth_sessions schema, and restore the established Aritmolab-sidebar user journey through the load balancer and Nginx.

Architecture: Keep the same installation-descriptor path and Compose project so Project A Qdrant/Ollama volumes and bind state remain authoritative. Close ingress, snapshot Project A configuration, configure Authentik and Supabase, replace the protected installation configuration transactionally at the same paths, validate privately, then open the production route and sidebar link.

Tech Stack: Authentik OAuth2/OIDC Authorization Code + PKCE, ThothII OIDC/session store, Supabase PostgreSQL schema migrations/RLS, Docker Compose, Nginx, load balancer, Aritmolab.


Preconditions

  • Project A automated and human reports are PASS and explicitly owner-approved.
  • The Mac rest_api installation passes source validation and connection diagnostics with its per-installation key.
  • The dual-key observation has lasted at least 48 hours and includes two scheduled 03:00 ETL cycles.
  • legacy-shared is revoked; v1 remains successful and the legacy credential is proven 401.
  • The current survey decision is SURVEY_GO_PROJECT_B, not only the private Project A decision.
  • Application SHA, workspace SHA, images, Project A report digest, and rollback configuration match the accepted evidence.
  • The production route is closed before authentication/session-storage changes.
  • All Authentik operations use the installed version's API/OpenAPI contract. Official current references include OAuth2/OIDC providers, provider property mappings, application bindings, and blueprint export; installed-version behavior wins over newer documentation.

Task 1: Freeze Project A and close ingress

Files:

  • Read: accepted Project A report
  • Create: protected Project B transaction root

Step 1: Verify exact Project A state

Run status, doctor, auth check, workspace inspect, vector inspect, Pi status/test, Git SHA, and image identity checks from Project A. Expected: all match the accepted report.

Step 2: Create a protected transaction root

Use mktemp -d under the survey-approved protected parent, mode 0700. Record its path and do not place it in Git.

Step 3: Close production and temporary ingress

Keep or restore a maintenance response at the production ThothII route. Disable the optional Project A test route before changing authentication unless it is needed for a separately approved private preflight. Confirm neither route reaches ThothII.

Step 4: Stop and back up Project A

<tht> --installation <stable-installation-path> backup \
  --output <project-b-transaction-root>/project-a-backup.tar --drain
<tht> --installation <stable-installation-path> stop

Verify the archive using the controller's manifest/checksum contract. Preserve a copy of the exact installation descriptor, env file, override files, auth directory, Nginx fragment, load-balancer route, Aritmolab sidebar file/revision, and relevant Authentik export metadata.

Task 2: Decide exact Authentik names and roles

Files:

  • Create: protected authentik-change-manifest.yaml

Step 1: Select the final public origin

Use the live survey result, not historical .it/.com assumptions. Record exactly one HTTPS origin and callback <origin>/api/auth/oidc/callback.

Step 2: Select exact groups

Default to dedicated TOT Users and TOT Admin. Reuse existing groups only if their membership semantics match and the owner approves. Record exact case-sensitive names.

Step 3: Define least privilege

Map user group → user, admin group → admin. Define a separate service account/token with only the installed Authentik permission needed to view exact group objects. No write, user-management, directory-administration, or superuser permission.

Step 4: Obtain owner approval of the manifest

The manifest contains object names, slugs, intended bindings, callback, scopes, grant types, credential destinations, and rollback action—but no secret values. Do not mutate Authentik before approval.

Task 3: Export and prepare Authentik

Files:

  • Create: protected pre-change Authentik export
  • Modify: Authentik objects named in the approved manifest

Step 1: Export relevant configuration

Use the installed version's supported blueprint/API export. A worker command such as ak export_blueprint is valid only if present in that version. Protect export mode 0600; remember write-only provider secrets are not included, so backup their custody separately without printing.

Step 2: Verify API credential scope

Use a read-only call to list relevant groups/applications. Expected: administrative creation access for the setup identity and a distinct path for the future group-view service account. Stop if the credential is missing or ambiguous.

Step 3: Create or confirm exact groups

Create missing dedicated groups, or record approved existing group IDs. Do not bulk-copy LDAP or unrelated Authentik memberships.

Step 4: Create the group-catalog service account

Grant only exact group-view permission. Create its token through the approved protected-secret mechanism; write it directly to the ThothII secret destination without displaying it.

Step 5: Create the OIDC provider

Configure a confidential OAuth2/OIDC provider with the exact callback, issuer mode observed as appropriate, Authorization Code, PKCE support, and Device Code only when required for tht auth check --interactive and supported by the installed release. Do not enable implicit flow.

Step 6: Configure scopes and direct groups claim

Select openid, profile, and email. Inspect a disposable identity's decoded claim keys through a protected verifier; retain only a redacted shape. Expected: ID token includes direct non-empty groups: [string, ...].

If the installed default profile mapping already provides that exact claim, reuse it. Otherwise add a provider scope/property mapping under the requested profile scope that returns:

return {"groups": [group.name for group in request.user.ak_groups.all()]}

Verify the installed mapping merge semantics before activation. Do not add a custom unrequested scope because ThothII requests only openid, profile, and email.

Step 7: Create the Authentik application

Bind it to the provider. Configure display metadata according to local Aritmolab conventions. Do not use an Authentik proxy provider or Nginx forward-auth for ThothII.

Step 8: Create and store the client secret

Write the client secret directly into the protected ThothII secret bundle key THT_OIDC_CLIENT_SECRET. Store the service-account token as THT_AUTHENTIK_API_TOKEN. Never place either value in the change manifest, shell history, Compose environment, or evidence.

Task 4: Prepare Supabase schema roles and backup

Files:

  • Read: harness/tht/migrations/sessions/001_schema.sql
  • Read: harness/tht/migrations/sessions/002_security.sql
  • Create: protected Supabase backup/evidence
  • Create: runtime and migrator credential files

Step 1: Confirm the database/schema boundary

Expected: use the surveyed existing Supabase PostgreSQL database; clinical data remains in datawarehouse; application sessions use schema thoth_sessions; no new database is created.

Step 2: Back up database metadata/data consistently

Use the existing Supabase/PostgreSQL backup procedure before DDL. Record backup ID, timestamp, checksum, and restore command. Do not put a dump in the Git repository.

Step 3: Create or validate dedicated roles

Create one migrator login and one runtime login according to the migration contract. The runtime role must not be superuser, owner, BYPASSRLS, CREATEROLE, CREATEDB, or a member of DWH write roles. The migrator credential remains unavailable to core.

Step 4: Write protected credential files

Create separate mode-0640 runtime-password, migrator-password, and session-CA files with surveyed ownership. Do not use command-line password arguments.

Step 5: Confirm PostgREST exclusion before migration

Record the exact exposed schema list. Expected: thoth_sessions absent. If the system exposes all schemas implicitly, stop and resolve the boundary before migration.

Task 5: Prepare the stable Project B installation configuration

Files:

  • Modify at the same stable paths: operator env, installation descriptor, authentication directory
  • Create: reviewed session-server override copied from deploy/compose.session-server.yaml.example
  • Create: protected server-session workspace config copied from deploy/workspaces/server-sessions.yaml.example

Step 1: Preserve the Compose project name

The native controller derives the project name from the absolute installation-descriptor path. Keep that exact path. Do not point Project B at a second descriptor path, because that would create new Qdrant/Ollama named volumes instead of using the Project A accepted state.

Step 2: Stage Project B files beside the live files

Prepare new env/descriptor/override/auth inputs in the protected transaction root. Add the session DB host/port/existing database name/runtime role/migrator role/TLS mode and three secret source paths. Use verify-full where hostname/SAN permits; any verify-ca exception requires explicit survey evidence and owner approval.

Step 3: Add the server-session override

Copy the current example to a reviewed local file and add it to the existing stable descriptor's overrides before the Git transport override ordering required by the installation. Do not edit the tracked example.

Step 4: Replace local auth state transactionally

With the stack stopped, move the complete Project A auth directory into the protected transaction root, recreate an empty private directory at the same path/ownership/mode, and configure OIDC:

<tht> --installation <stable-installation-path> auth configure \
  --mode oidc --public-url <final-https-origin> \
  --issuer <authentik-issuer> --client-id <oidc-client-id> \
  --authentik-base-url <authentik-base-url> \
  --user-group '<exact-user-group>' --admin-group '<exact-admin-group>'

Expected: non-secret auth.yaml only; secrets resolved from the protected bundle.

Step 5: Atomically install staged path-only files

Use same-filesystem rename and preserve required ownership/mode. Keep the Project A originals in the transaction root. Run update --check-only; on failure restore the originals immediately.

Task 6: Run and verify session migrations

Files:

  • Modify through one-shot migrator: existing database schema thoth_sessions

Step 1: Validate migration rendering

<tht> --installation <stable-installation-path> update --check-only

Expected: core and session-migrate resolve the same core image; core lacks migrator password; only the one-shot service sees it.

Step 2: Run migrations once

<tht> --installation <stable-installation-path> sessions migrate --yes

Expected JSON: "pending":[] and "drifted":[]; applied may list 001 and 002 on first use.

Step 3: Run migration status/idempotency again

Run the same command. Expected: no new application and both pending/drifted remain empty.

Step 4: Verify database security

Through bounded catalog queries, prove forced RLS, policies on session tables, runtime role without BYPASSRLS/ownership/DDL, migrator absent from core, and no runtime privileges on unrelated schemas.

Step 5: Recheck PostgREST exclusion

Expected: thoth_sessions still absent from exposed schemas and REST endpoints cannot address it.

Task 7: Validate Authentik and start privately

Files:

  • Record: Project B protected evidence

Step 1: Run static configuration validation

Run update --check-only and redacted auth status --json. Expected: mode OIDC, exact public origin, issuer/client ID/group names, and no secret values.

Step 2: Start while public ingress remains closed

<tht> --installation <stable-installation-path> start
<tht> --installation <stable-installation-path> status
<tht> --installation <stable-installation-path> auth check --json
<tht> --installation <stable-installation-path> doctor --json
<tht> --installation <stable-installation-path> pi test

Expected: OIDC discovery/issuer/JWKS, client-secret access, Authentik catalog token, exact mapped groups, PostgreSQL session storage, workspace, services, workflow, and Pi pass.

Step 3: Run interactive device check when supported

<tht> --installation <stable-installation-path> auth check --interactive

Expected: approved identity completes Device Authorization and direct groups claim validates. If the installed provider does not support device flow, record PENDING rather than substituting a token.

Task 8: Prepare Nginx, TLS, load balancer, and sidebar

Files:

  • Modify only survey-approved ThothII Nginx fragment
  • Modify only survey-approved load-balancer route
  • Modify only exact Aritmolab sidebar source when its target must change

Step 1: Prepare direct-OIDC Nginx configuration

Follow docs/install/reverse-proxy-nginx.md, direct OIDC section. Required behavior: no auth_request, no callback rewrite, frontend loopback upstream, Host and HTTPS forwarded headers, HTTP/1.1, buffering/cache off, long SSE read timeout, and X-Accel-Buffering: no.

Step 2: Validate the managed certificate

Expected: SAN matches final hostname, validity is current, chain is trusted by approved clients, private-key permissions match local policy, and renewal/generation ownership is recorded. Never copy the key into ThothII.

Step 3: Validate Nginx without opening traffic

sudo nginx -t
curl --fail http://127.0.0.1:<frontend-port>/health

Use local --resolve/Host tests only when they do not bypass the identity behavior being tested.

Step 4: Prepare the load-balancer route

Configure backend/health/TLS according to the surveyed owner procedure, initially disabled or operator-only. Confirm it targets Nginx, never core/Qdrant/Ollama directly.

Step 5: Preserve the Aritmolab link contract

If the existing sidebar target already equals the final origin/path, leave source unchanged and record proof. Otherwise make the smallest reviewed change, test it in Aritmolab's own test/build system, and commit in that repository before deployment.

Task 9: Open ingress and run OIDC acceptance

Files:

  • Complete: docs/testing/psd-server-project-b-manual.md

Step 1: Reload Nginx through the established mechanism

Run nginx -t immediately before reload. Expected: reload succeeds and unrelated virtual hosts remain healthy.

Step 2: Enable the final load-balancer route

Expected: HTTP redirects to HTTPS; TLS is valid; /api/auth/oidc/login redirects to the correct Authentik provider; callback returns to the exact public origin.

Step 3: Test ordinary and admin identities

Start at Aritmolab, authenticate once, then use the sidebar. Expected: no second credential prompt; ordinary user can use sessions but receives 403 for admin operations; admin has only documented permissions.

Step 4: Test no-role and malformed cases

An identity with no mapped group authenticates but receives no application role/403. Missing, malformed, indirect, or ambiguous group claims fail closed with generic browser errors and redacted diagnostics. Do not retain raw claims.

Step 5: Test logout and restart

Verify ThothII logout revokes its own cookie. Document whether the Authentik SSO session remains and therefore allows immediate re-login without credentials; do not claim global logout unless configured and tested. Restart core and verify expected OIDC session behavior.

Task 10: Verify PostgreSQL ownership and complete F1-F8

Files:

  • Create: protected Project B session evidence

Step 1: Create sessions under two identities

Expected: ordinary users see only their own sessions; cross-user access returns the documented not-found boundary; admin behavior matches session.read_all/manage_all permissions.

Step 2: Verify RLS with the runtime path

Use application/API tests and bounded catalog evidence. Never disable RLS for diagnosis.

Step 3: Complete one harmless OIDC PSD session

Use the same approved read-only question or another owner-approved one. Complete F1-F8, validate final SQL, resume once, and confirm session/artifacts/decisions are stored in thoth_sessions.

Step 4: Verify ephemeral boundaries

Expected: no chat transcript or SSE stream stored as session artifacts; no vectors in PostgreSQL; Qdrant remains the semantic store.

Task 11: Test controlled failures and rollback

Files:

  • Record: protected rollback evidence

Step 1: Test a reversible provider/catalog failure

Use a controlled, owner-approved method such as a temporary disabled test credential or test object. Expected: auth diagnostics and browser login fail closed, redacted, then pass after restoration. Never break unrelated Authentik applications.

Step 2: Rehearse ingress-first rollback

Close the production route, validate Nginx restoration commands, and prove the protected Project A configuration snapshot is complete. A full rollback need not destroy thoth_sessions.

Step 3: Verify additive database rollback boundary

Expected: rollback leaves schema/data intact for evidence and future recovery. No automatic DROP SCHEMA or role deletion.

Task 12: Close Project B

Files:

  • Complete: docs/testing/evidence/psd-server-project-b-report-template.md

Step 1: Run final diagnostics

Run status, doctor, auth check, interactive check when supported, workspace inspect, vector inspect, Pi test, Nginx validation, load-balancer health, Supabase migration/security checks, and sidebar test.

Step 2: Remove the Project A temporary endpoint

Remove only its load-balancer route, Nginx fragment, and managed certificate reference according to their owners. Validate/reload and prove the hostname no longer routes.

Step 3: Complete the human guide and report

Every mandatory row must be PASS. Record exact source/image/workspace identities, Authentik object names/IDs, Supabase database plus thoth_sessions, public origin, Aritmolab revision, report digest, and PROJECT_B_PASS or PROJECT_B_FAIL.

Step 4: Handle FAIL safely

On FAIL, close ingress first. Restore Project A files at the same stable paths, move OIDC auth state to protected evidence, restore the local-auth directory, validate, and start Project A privately. Disable new Authentik objects; do not delete them or drop the session schema automatically.