18 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.
- 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.