438 lines
18 KiB
Markdown
438 lines
18 KiB
Markdown
# 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](https://docs.goauthentik.io/add-secure-apps/providers/oauth2/),
|
|
[provider property mappings](https://docs.goauthentik.io/add-secure-apps/providers/property-mappings/),
|
|
[application bindings](https://docs.goauthentik.io/add-secure-apps/applications/manage_apps/), and
|
|
[blueprint export](https://docs.goauthentik.io/customize/blueprints/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**
|
|
|
|
```bash
|
|
<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:
|
|
|
|
```python
|
|
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:
|
|
|
|
```bash
|
|
<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**
|
|
|
|
```bash
|
|
<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**
|
|
|
|
```bash
|
|
<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**
|
|
|
|
```bash
|
|
<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**
|
|
|
|
```bash
|
|
<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**
|
|
|
|
```bash
|
|
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.
|