docs: design per-installation DWH REST auth
This commit is contained in:
@@ -29,7 +29,8 @@ Authentik, Aritmolab o repository esterni.
|
|||||||
|
|
||||||
- Current activity: `1`
|
- Current activity: `1`
|
||||||
- Title: Rotate or revoke the exposed DWH credential safely
|
- Title: Rotate or revoke the exposed DWH credential safely
|
||||||
- Resume from: Activity 1, identify the accountable credential owner and the credential type without revealing its value
|
- Resume from: Activity 1, review the written per-installation authentication design, then prepare
|
||||||
|
its executable implementation and rotation plan
|
||||||
- Discussion rule: una sola attività può essere `IN_DISCUSSION`
|
- Discussion rule: una sola attività può essere `IN_DISCUSSION`
|
||||||
- Allowed states: `PENDING`, `IN_DISCUSSION`, `BLOCKED`, `PASS`
|
- Allowed states: `PENDING`, `IN_DISCUSSION`, `BLOCKED`, `PASS`
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ Authentik, Aritmolab o repository esterni.
|
|||||||
|
|
||||||
| ID | Attività | Stato | Responsabile | Prossimo gate |
|
| ID | Attività | Stato | Responsabile | Prossimo gate |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| 1 | Rotazione controllata della credenziale DWH esposta | `IN_DISCUSSION` | Unassigned | Identificare owner e tipo di credenziale |
|
| 1 | Rotazione controllata della credenziale DWH esposta | `IN_DISCUSSION` | Proprietario del progetto | Revisionare la specifica scritta e approvare il piano eseguibile |
|
||||||
| 2 | Assegnazione dei responsabili dei componenti condivisi | `PENDING` | Unassigned | Elenco owner confermato |
|
| 2 | Assegnazione dei responsabili dei componenti condivisi | `PENDING` | Unassigned | Elenco owner confermato |
|
||||||
| 3 | Risoluzione del dominio pubblico `.it` oppure `.com` | `PENDING` | Unassigned | Origine autorevole documentata |
|
| 3 | Risoluzione del dominio pubblico `.it` oppure `.com` | `PENDING` | Unassigned | Origine autorevole documentata |
|
||||||
| 4 | Topologia e responsabilità del load balancer | `PENDING` | Unassigned | Route, health, TLS, rollback e allowlist verificati |
|
| 4 | Topologia e responsabilità del load balancer | `PENDING` | Unassigned | Route, health, TLS, rollback e allowlist verificati |
|
||||||
@@ -65,7 +66,7 @@ Authentik, Aritmolab o repository esterni.
|
|||||||
## Activity 1: Rotate or revoke the exposed DWH credential safely
|
## Activity 1: Rotate or revoke the exposed DWH credential safely
|
||||||
|
|
||||||
- Status: `IN_DISCUSSION`
|
- Status: `IN_DISCUSSION`
|
||||||
- Accountable owner: Unassigned
|
- Accountable owner: Proprietario del progetto (confermato dall'utente)
|
||||||
- Objective: sostituire o revocare in modo controllato la credenziale DWH comparsa nell'output
|
- Objective: sostituire o revocare in modo controllato la credenziale DWH comparsa nell'output
|
||||||
interno del survey, senza interrompere consumer legittimi e senza esporne nuovamente il valore.
|
interno del survey, senza interrompere consumer legittimi e senza esporne nuovamente il valore.
|
||||||
- Why this is required: la credenziale deve essere considerata compromessa; non può essere usata
|
- Why this is required: la credenziale deve essere considerata compromessa; non può essere usata
|
||||||
@@ -89,14 +90,82 @@ Authentik, Aritmolab o repository esterni.
|
|||||||
- timestamp e risultati dei test positivi e negativi;
|
- timestamp e risultati dei test positivi e negativi;
|
||||||
- conferma di revoca della credenziale precedente;
|
- conferma di revoca della credenziale precedente;
|
||||||
- procedura di rollback e relativo esito.
|
- procedura di rollback e relativo esito.
|
||||||
- Discussion notes: il survey ha osservato la credenziale durante l'analisi della configurazione
|
- Discussion notes:
|
||||||
Nginx relativa al DWH. Non è ancora provato se sia una chiave API, un header condiviso o un altro
|
- la credenziale osservata è stata identificata, senza rileggerne il valore, come chiave statica
|
||||||
tipo di secret; non va confusa automaticamente con una password PostgreSQL.
|
`X-API-Key` applicata da Nginx alla route DWH REST `/dwh/`; è distinta dalle password PostgreSQL;
|
||||||
- Decision: No decision recorded
|
- `/home/chirone/chirone/etl` documenta PostgREST come integrazione HTTP esterna, mentre i processi
|
||||||
- Blockers: owner non identificato; tipo di credenziale non verificato; consumer e supporto alla
|
ETL e Superset usano PostgreSQL diretto;
|
||||||
doppia credenziale non inventariati.
|
- il vecchio ThothII e Chirone WP3 usano PostgreSQL diretto nei profili locali/server; Supabase
|
||||||
- Next step: il proprietario identifica il responsabile della credenziale della route DWH e indica
|
Studio è un pannello amministrativo e non appartiene al data-plane applicativo;
|
||||||
se appartiene al team Nginx, Supabase/PostgreSQL o a un altro team.
|
- il design approvato resta invariato: il Mac usa `rest_api`, il nuovo ThothII sul server PSD usa
|
||||||
|
`postgres_direct` con ruolo DWH dedicato e realmente read-only;
|
||||||
|
- la ricognizione dei repository ha rilevato materiale sensibile hardcoded in file tracciati,
|
||||||
|
senza riportarne i valori. La bonifica e la rotazione dei segreti coinvolti restano obbligatorie.
|
||||||
|
- il censimento statico non trova consumer `/dwh/` in ETL, Superset o nel Chirone WP3 attivo:
|
||||||
|
usano PostgreSQL diretto. Il vecchio container `thothii-core-1` è configurato con
|
||||||
|
`transport: direct`; i due ThothII restano comunque tecnicamente capaci di usare REST;
|
||||||
|
- i log Nginx redatti provano 18.523 richieste `/dwh/` dal 23 luglio al 19 agosto 2026:
|
||||||
|
18.481 hanno user-agent classificato `python-requests` e gli endpoint RPC corrispondono
|
||||||
|
prevalentemente a introspezione e campionamento Thoth. La sorgente è una sola, privata e
|
||||||
|
compatibile con un proxy/load balancer; non prova che esista un solo client finale;
|
||||||
|
- l'ipotesi che il traffico sia generato dal job ETL delle 03:00 è smentita: nella finestra
|
||||||
|
02:30–03:30 Europe/Rome non compare nessuna richiesta `/dwh/`. Il 99,37% del traffico è
|
||||||
|
concentrato il 13 agosto tra le 17:38 e le 20:03;
|
||||||
|
- la firma del 13 agosto corrisponde a undici preprocessing Thoth: undici `list_tables`, e
|
||||||
|
per ciascuna esecuzione 163 chiamate a ognuno dei tre RPC per-tabella più 1.180 `top_values`,
|
||||||
|
cioè 1.670 richieste per ciclo. `PROJECT_STATE.md` registra proprio il preprocessing PSD live
|
||||||
|
del 13 agosto su 163 tabelle, con più rerun e correzioni emerse durante l'esecuzione;
|
||||||
|
- il DAG ETL `nightly_etl_orchestrator` è schedulato con `0 3 * * *`, ma scrive il DWH tramite
|
||||||
|
PostgreSQL/`psycopg2` diretto. Nel codice tracciato non chiama `/dwh/`, gli RPC Thoth o
|
||||||
|
`tht workspace preprocess`, né emerge un trigger indiretto verso Thoth;
|
||||||
|
- la configurazione Nginx nominalmente attiva accetta una sola chiave tramite confronto letterale
|
||||||
|
in un endpoint `auth_request`. Non esiste una mappa a più chiavi: la doppia credenziale richiede
|
||||||
|
un refactor, backup, `nginx -t`, reload e rollback in una fase di mutazione autorizzata.
|
||||||
|
- il proprietario conferma che il ThothII sul Mac deve continuare a usare REST e che sono previste
|
||||||
|
molte altre installazioni remote, senza tunnel SSH verso Supabase. `/dwh/` è quindi
|
||||||
|
un'interfaccia remota stabile e multi-client, non una compatibilità temporanea.
|
||||||
|
- il proprietario decide di mantenere il certificato TLS corrente. L'endpoint esterno REST
|
||||||
|
presenta lo stesso certificato self-issued di Nginx, valido fino al 21 giugno 2027 e con SAN
|
||||||
|
per `supabase-aritmolab.policlinicosandonato.it`; non copre un eventuale dominio `.com`;
|
||||||
|
- il manuale di installazione deve trattare `TLS_CA_FILE` come necessario per ogni client che
|
||||||
|
non abbia già quel certificato nel proprio trust store, spiegando consegna affidabile,
|
||||||
|
verifica del fingerprint, rinnovo e aggiornamento coordinato delle installazioni;
|
||||||
|
- non esiste un ambiente di test. La rotazione dovrà quindi usare una verifica production-safe:
|
||||||
|
backup, finestra dual-key, RPC `ping` senza dati clinici, test positivo/negativo e rollback.
|
||||||
|
- il proprietario approva un componente `dwh-auth` riutilizzabile ma opzionale, incluso nel
|
||||||
|
repository senza modificare il protocollo dei client portabili o il CLI `tht`;
|
||||||
|
- su PSD `dwh-auth` avrà un lifecycle `systemd` indipendente dallo stack ThothII e comunicherà
|
||||||
|
con Nginx tramite socket Unix. Lo stop o la sostituzione di ThothII non dovrà interrompere i
|
||||||
|
client REST;
|
||||||
|
- il registro sarà composto da file protetti, versionati e aggiornati atomicamente, con un file
|
||||||
|
per generazione della chiave. Conterrà digest SHA-256 di segreti casuali da almeno 256 bit e
|
||||||
|
metadati non sensibili, senza SQLite o nuove dipendenze runtime;
|
||||||
|
- le chiavi saranno assegnate alle installazioni, non alle persone. Saranno prive di scadenza
|
||||||
|
predefinita, con scadenza opzionale e revoca manuale;
|
||||||
|
- creazione e import leggeranno o scriveranno soltanto file protetti. Nessun segreto sarà
|
||||||
|
accettato come argomento, stampato o inserito in log, JSON, documenti o repository;
|
||||||
|
- la gestione sarà fail-closed: credenziali non valide riceveranno `401`, mentre guasti del
|
||||||
|
servizio o del registro saranno mappati a `503` senza fallback permissivo;
|
||||||
|
- il proprietario conferma che le sessioni, gli indici e le cache del vecchio ThothII erano solo
|
||||||
|
test e non richiedono migrazione o attività di salvaguardia dedicate. Restano necessari il
|
||||||
|
rollback della route DWH condivisa e il rispetto del gate esplicito prima di fermare lo stack;
|
||||||
|
- il design approvato è scritto in
|
||||||
|
`docs/superpowers/specs/2026-08-20-dwh-rest-per-installation-auth-design.md` e richiede una
|
||||||
|
revisione del proprietario prima del piano di implementazione.
|
||||||
|
- Decision: il nuovo ThothII server userà PostgreSQL diretto; il Mac e le future installazioni
|
||||||
|
remote useranno REST. La chiave condivisa corrente non è un modello finale accettabile: la
|
||||||
|
rotazione esposta userà una transizione a doppia credenziale e l'architettura target assegnerà
|
||||||
|
un'identità revocabile distinta a ogni installazione. Supabase Studio non sarà usato come
|
||||||
|
trasporto. Il proprietario del progetto è accountable per coordinare la rotazione. Il meccanismo
|
||||||
|
target è il servizio server-only `dwh-auth`, indipendente dallo stack ThothII, con registro a file
|
||||||
|
e una chiave revocabile per installazione.
|
||||||
|
- Blockers: la specifica scritta attende la revisione del proprietario; mancano ancora il piano
|
||||||
|
eseguibile, la sorgente protetta per importare la chiave legacy, la procedura realizzata di
|
||||||
|
aggiornamento/verifica del Mac, la documentazione TLS completa, l'autorizzazione alla mutazione
|
||||||
|
e la revoca provata della vecchia chiave.
|
||||||
|
- Next step: dopo l'approvazione della specifica scritta, preparare il piano eseguibile di
|
||||||
|
implementazione e rotazione dual-key con test automatici, aggiornamento Mac, prova
|
||||||
|
positiva/negativa e rollback. Nessuna modifica Nginx avviene durante questa discussione.
|
||||||
|
|
||||||
## Activity 2: Identify accountable owners for shared components
|
## Activity 2: Identify accountable owners for shared components
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,383 @@
|
|||||||
|
# DWH REST Per-Installation Authentication — Design
|
||||||
|
|
||||||
|
**Date:** 2026-08-20
|
||||||
|
|
||||||
|
**Status:** Approved in discussion; awaiting review of this written specification
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Replace the single static `X-API-Key` currently protecting the PSD `/dwh/` route with a reusable,
|
||||||
|
server-side authentication component that assigns one independently revocable credential to each
|
||||||
|
ThothII installation.
|
||||||
|
|
||||||
|
The design must preserve the existing portable connector contract. A ThothII installation on
|
||||||
|
macOS, Windows, or Linux continues to send `X-API-Key` over HTTPS when its selected transport is
|
||||||
|
`rest_api`. Installations using `postgres_direct` or `ssh_tunnel` are unaffected.
|
||||||
|
|
||||||
|
The component belongs in the ThothII source repository so that it can be reused on another DWH
|
||||||
|
server, but it is optional server infrastructure. It is not part of the portable application
|
||||||
|
runtime and is not started or stopped by `tht`.
|
||||||
|
|
||||||
|
## Confirmed context
|
||||||
|
|
||||||
|
- The exposed credential must be considered compromised and must eventually be revoked.
|
||||||
|
- The current PSD route accepts one shared static `X-API-Key` through Nginx `auth_request`.
|
||||||
|
- The Mac installation must continue to access PSD through REST.
|
||||||
|
- Future remote ThothII installations will also use REST and cannot depend on an SSH tunnel.
|
||||||
|
- The new ThothII installation on the PSD server will use `postgres_direct` with a dedicated,
|
||||||
|
demonstrably read-only DWH role.
|
||||||
|
- The current self-issued TLS certificate remains in use. Clients that do not already trust its
|
||||||
|
issuer require a separately delivered `TLS_CA_FILE` and must verify the approved fingerprint.
|
||||||
|
- There is no separate test environment. This is accepted and is not itself a blocker.
|
||||||
|
- Existing ThothII sessions, derived indexes, and model caches are test data. They are not migrated
|
||||||
|
or protected by session-specific backup work.
|
||||||
|
- The legacy stack remains unchanged until the deployment program reaches its explicit stop gate.
|
||||||
|
This is an authorization boundary, not a session-preservation requirement.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This design includes:
|
||||||
|
|
||||||
|
- the reusable `dwh-auth` service and its administrative command;
|
||||||
|
- a protected file-based credential registry;
|
||||||
|
- Nginx `auth_request` integration;
|
||||||
|
- one credential identity per ThothII installation;
|
||||||
|
- creation, delivery, rotation, optional expiry, and revocation;
|
||||||
|
- a dual-key transition from the exposed shared credential;
|
||||||
|
- generic and PSD-specific operator documentation;
|
||||||
|
- automated tests and production-safe acceptance checks.
|
||||||
|
|
||||||
|
This design does not:
|
||||||
|
|
||||||
|
- change the ThothII REST header or connector protocol;
|
||||||
|
- add DWH credential administration to the portable `tht` CLI;
|
||||||
|
- place server credentials in a workspace repository;
|
||||||
|
- replace PostgreSQL grants, RLS, or the PostgREST read-only role boundary;
|
||||||
|
- preserve or migrate legacy ThothII sessions, Qdrant indexes, or Ollama caches;
|
||||||
|
- change the current certificate or resolve the separate `.it` versus `.com` public-origin issue;
|
||||||
|
- authorize any current server, Nginx, database, or service mutation.
|
||||||
|
|
||||||
|
## Alternatives considered
|
||||||
|
|
||||||
|
### Extend the portable `tht` CLI
|
||||||
|
|
||||||
|
This would provide a single operator command surface, but it would distribute server-only DWH
|
||||||
|
administration code to every macOS and Windows installation. The DWH route also has a lifecycle
|
||||||
|
independent from the local ThothII application. This option is rejected.
|
||||||
|
|
||||||
|
### Maintain a manual Nginx key map
|
||||||
|
|
||||||
|
This is initially small, but it encourages plaintext credentials in Nginx configuration and makes
|
||||||
|
atomic rotation, redacted inventory, and reliable revocation harder. This option is rejected.
|
||||||
|
|
||||||
|
### Use SQLite
|
||||||
|
|
||||||
|
SQLite is not a declared project dependency and is unnecessary for the expected registry size and
|
||||||
|
write rate. Introducing a database would add packaging, migration, backup, and corruption-recovery
|
||||||
|
work without improving the required contract. This option is rejected.
|
||||||
|
|
||||||
|
### Selected approach
|
||||||
|
|
||||||
|
Add a standalone, standard-library Go component with a protected file registry. On PSD it runs as
|
||||||
|
an independent `systemd` service and communicates with host Nginx through a Unix socket.
|
||||||
|
|
||||||
|
## Component and lifecycle boundary
|
||||||
|
|
||||||
|
The repository will contain a separate component, expected under `tools/dwh-auth/`, plus generic
|
||||||
|
deployment templates and documentation. Its build and tests are isolated from the portable
|
||||||
|
application entrypoints.
|
||||||
|
|
||||||
|
On PSD:
|
||||||
|
|
||||||
|
- `dwh-auth` runs under a dedicated, unprivileged system account;
|
||||||
|
- `systemd` owns its lifecycle;
|
||||||
|
- the service is not included in the canonical ThothII Compose stack;
|
||||||
|
- the registry resides outside both the Git checkout and ThothII application data;
|
||||||
|
- the service receives read-only access to active credential records;
|
||||||
|
- Nginx is the only runtime caller and reaches it through a permission-restricted Unix socket;
|
||||||
|
- stopping, updating, or replacing ThothII does not interrupt `/dwh/` authentication.
|
||||||
|
|
||||||
|
The same source may be deployed on another Linux DWH server. Platform-specific service packaging
|
||||||
|
beyond the approved PSD `systemd` deployment is not required by this implementation.
|
||||||
|
|
||||||
|
## Credential model
|
||||||
|
|
||||||
|
One credential identifies one ThothII installation, not one human user. An installation may have
|
||||||
|
more than one temporarily active generation during rotation.
|
||||||
|
|
||||||
|
The external key has two components:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<public-key-id>.<random-secret>
|
||||||
|
```
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- the public key ID is generated by the tool and contains only a strict safe alphabet;
|
||||||
|
- the secret is generated with the operating system cryptographic random source;
|
||||||
|
- the secret has at least 256 bits of entropy and uses an unambiguous transport-safe encoding;
|
||||||
|
- the complete header has a fixed maximum length;
|
||||||
|
- installation IDs are operator-provided, non-secret, unique labels and must not contain personal
|
||||||
|
or clinical information;
|
||||||
|
- descriptions are optional non-secret operator metadata.
|
||||||
|
|
||||||
|
Because the secret is a uniformly random 256-bit value rather than a human password, the registry
|
||||||
|
stores a SHA-256 digest and verifies it with a constant-time comparison. No password hashing or
|
||||||
|
external cryptographic runtime dependency is needed.
|
||||||
|
|
||||||
|
## Protected file registry
|
||||||
|
|
||||||
|
The registry has versioned active and revoked records:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<registry-root>/
|
||||||
|
active/<public-key-id>.json
|
||||||
|
revoked/<public-key-id>.json
|
||||||
|
```
|
||||||
|
|
||||||
|
An active record contains:
|
||||||
|
|
||||||
|
- schema version;
|
||||||
|
- public key ID;
|
||||||
|
- installation ID and optional description;
|
||||||
|
- encoded SHA-256 digest;
|
||||||
|
- creation timestamp;
|
||||||
|
- optional expiry timestamp, absent by default.
|
||||||
|
|
||||||
|
A revoked record additionally contains the revocation timestamp and a non-secret reason. The
|
||||||
|
original key never appears in either directory.
|
||||||
|
|
||||||
|
Multiple key IDs may refer to the same installation ID during a rotation. Creation uses an
|
||||||
|
exclusive temporary file, file flush, directory-safe atomic rename, restrictive ownership and
|
||||||
|
mode checks, and refusal of symlinks or unsafe paths. Revocation atomically moves a record from
|
||||||
|
`active` to `revoked` on the same filesystem. A malformed or unsafe record never authenticates.
|
||||||
|
|
||||||
|
The runtime service does not update registry files. Successful and failed use is recorded only in
|
||||||
|
the sanitized service journal, avoiding per-request writes and keeping the service's registry
|
||||||
|
access read-only.
|
||||||
|
|
||||||
|
## Administrative interface
|
||||||
|
|
||||||
|
The standalone binary provides a server-only administrative surface equivalent to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
dwh-auth key create --installation-id ID --description TEXT --output ABSOLUTE_FILE
|
||||||
|
dwh-auth key import --installation-id ID --from-file ABSOLUTE_FILE
|
||||||
|
dwh-auth key list [--json]
|
||||||
|
dwh-auth key revoke --key-id ID --reason TEXT
|
||||||
|
dwh-auth key status --key-id ID [--json]
|
||||||
|
dwh-auth check
|
||||||
|
```
|
||||||
|
|
||||||
|
The exact syntax will be frozen in the implementation plan and tests. The interface must obey
|
||||||
|
these invariants:
|
||||||
|
|
||||||
|
- secrets are never accepted as command-line values;
|
||||||
|
- `create` writes the generated credential once to a new absolute file with restrictive access;
|
||||||
|
- `import` reads an existing protected file without displaying its content;
|
||||||
|
- normal stdout contains only non-secret identifiers, abbreviated fingerprints, status, and
|
||||||
|
paths;
|
||||||
|
- JSON output is pristine and contains no credential value or digest;
|
||||||
|
- an existing output file is never overwritten;
|
||||||
|
- listing and status commands never expose digests;
|
||||||
|
- errors and logs pass explicit secret-leak regression tests.
|
||||||
|
|
||||||
|
## Provisioning and delivery
|
||||||
|
|
||||||
|
The operator creates one key for each installation and transfers it through an approved protected
|
||||||
|
channel, such as an organizational password manager, a secret manager or MDM, or authenticated
|
||||||
|
file transfer when the recipient has suitable access. Email, ordinary chat, and ticket bodies are
|
||||||
|
not approved delivery channels.
|
||||||
|
|
||||||
|
On the recipient installation:
|
||||||
|
|
||||||
|
- the normal interactive path saves the value through authenticated Workspace management, where
|
||||||
|
ThothII keeps it in the installation-local encrypted workspace vault;
|
||||||
|
- a supported headless deployment may place it in a protected file referenced by `API_KEY_FILE`;
|
||||||
|
- the credential never enters the shared workspace repository or ordinary environment values;
|
||||||
|
- the private CA is delivered separately and configured with `TLS_CA_FILE` when the certificate
|
||||||
|
chain is not already trusted;
|
||||||
|
- the operator verifies the documented certificate fingerprint before trusting the CA file.
|
||||||
|
|
||||||
|
After configuration, the recipient runs the existing workspace connection test against the
|
||||||
|
harmless `/rpc/ping` diagnostic. Only after the server and recipient both confirm the public key
|
||||||
|
ID may the one-time delivery copy be removed according to the organization's secret-handling
|
||||||
|
procedure.
|
||||||
|
|
||||||
|
## Expiry and rotation
|
||||||
|
|
||||||
|
Keys have no automatic expiry by default. This avoids unplanned outages for remote installations
|
||||||
|
that may not be continuously administered. An optional expiry timestamp is supported for sites
|
||||||
|
whose policy requires it; an expired key is rejected without fallback. Administrative status
|
||||||
|
output warns about approaching expiries without exposing secrets.
|
||||||
|
|
||||||
|
Normal zero-downtime rotation is:
|
||||||
|
|
||||||
|
1. create a second key generation for the installation;
|
||||||
|
2. deliver and configure it on the client;
|
||||||
|
3. verify `/rpc/ping` and the sanitized server key ID;
|
||||||
|
4. revoke the earlier key;
|
||||||
|
5. prove that the revoked key receives `401`;
|
||||||
|
6. remove temporary delivery material after recipient confirmation.
|
||||||
|
|
||||||
|
## Request flow and Nginx contract
|
||||||
|
|
||||||
|
```text
|
||||||
|
remote ThothII
|
||||||
|
-> HTTPS /dwh/ with X-API-Key
|
||||||
|
-> Nginx auth_request subrequest
|
||||||
|
-> dwh-auth over a Unix socket
|
||||||
|
-> valid: Nginx proxies the original request to internal PostgREST
|
||||||
|
-> invalid or revoked: Nginx returns 401
|
||||||
|
-> authenticator fault: Nginx returns 503
|
||||||
|
```
|
||||||
|
|
||||||
|
`dwh-auth` authenticates only. It does not proxy the original request, read a clinical response,
|
||||||
|
or connect to PostgreSQL. PostgREST remains inaccessible as a public direct backend.
|
||||||
|
|
||||||
|
Nginx integration must:
|
||||||
|
|
||||||
|
- forward only the bounded credential header to the internal verification endpoint;
|
||||||
|
- suppress the original request body in the auth subrequest;
|
||||||
|
- discard client-supplied identity or audit headers;
|
||||||
|
- accept only the authenticator's success result;
|
||||||
|
- return the same generic `401` for missing, malformed, unknown, expired, and revoked keys;
|
||||||
|
- map authenticator or registry faults to `503`, never to permissive access;
|
||||||
|
- apply bounded failure-rate protection;
|
||||||
|
- log only timestamp, result, request metadata already approved for the route, and the verified
|
||||||
|
public key ID;
|
||||||
|
- preserve the existing PostgREST path and response contract for successful requests.
|
||||||
|
|
||||||
|
The authenticator returns a verified public key ID only after successful authentication. Nginx may
|
||||||
|
use that value in its sanitized access log but does not need to forward it to PostgREST.
|
||||||
|
|
||||||
|
## Fail-closed behavior
|
||||||
|
|
||||||
|
The following conditions deny access:
|
||||||
|
|
||||||
|
- header absent, duplicated, oversized, malformed, or encoded incorrectly;
|
||||||
|
- unknown public key ID;
|
||||||
|
- digest mismatch;
|
||||||
|
- revoked or expired record;
|
||||||
|
- unsafe permissions, symlink, malformed JSON, schema mismatch, or inconsistent record identity;
|
||||||
|
- unreadable registry;
|
||||||
|
- service startup or runtime failure.
|
||||||
|
|
||||||
|
Credential failures return a generic `401`. Infrastructure failures return `503` after the Nginx
|
||||||
|
mapping is applied. Neither response reveals whether an installation ID exists. Health reporting
|
||||||
|
is local-only and does not weaken the authentication decision.
|
||||||
|
|
||||||
|
## PSD dual-key transition
|
||||||
|
|
||||||
|
The exposed shared credential is represented temporarily as `legacy-shared`. It is imported only
|
||||||
|
from an approved protected source and is never placed in a command argument, terminal output,
|
||||||
|
document, or evidence file.
|
||||||
|
|
||||||
|
The production route does not switch to the new authenticator until all of the following hold:
|
||||||
|
|
||||||
|
1. protected backup and exact rollback instructions exist for the affected Nginx and service
|
||||||
|
configuration;
|
||||||
|
2. the new service passes local synthetic checks while disconnected from the public route;
|
||||||
|
3. `legacy-shared` passes `/rpc/ping` through the candidate authentication path;
|
||||||
|
4. a new per-installation Mac key passes the same diagnostic;
|
||||||
|
5. a random invalid key is rejected;
|
||||||
|
6. `nginx -t` passes and the authorized operator approves reload.
|
||||||
|
|
||||||
|
After the Mac uses its new credential successfully for the agreed observation window,
|
||||||
|
`legacy-shared` is revoked. Acceptance requires a positive test with the new key and a negative
|
||||||
|
test proving `401` with the legacy key. The new secret must not appear in service, Nginx, shell, or
|
||||||
|
application logs.
|
||||||
|
|
||||||
|
Rollback during the dual-key window restores only the reviewed authentication route and service
|
||||||
|
configuration needed to keep REST clients working. It does not preserve or restore legacy
|
||||||
|
ThothII sessions, indexes, or model caches.
|
||||||
|
|
||||||
|
## PostgreSQL authorization boundary
|
||||||
|
|
||||||
|
Successful API-key authentication is not proof of read-only database authorization. The existing
|
||||||
|
PostgREST role, exposed schemas, function privileges, default privileges, and direct network
|
||||||
|
reachability remain subject to the catalog-only survey activity. Project execution cannot treat
|
||||||
|
`dwh-auth` as a substitute for a demonstrably read-only `dwh_reader` boundary.
|
||||||
|
|
||||||
|
## Verification strategy
|
||||||
|
|
||||||
|
Automated tests use synthetic credentials and no clinical data. They cover:
|
||||||
|
|
||||||
|
- key generation entropy and syntax;
|
||||||
|
- create, import, list, status, optional expiry, rotation, and revocation;
|
||||||
|
- constant-time digest verification;
|
||||||
|
- missing, duplicate, malformed, oversized, unknown, expired, and revoked credentials;
|
||||||
|
- corrupt records, unsafe modes, symlinks, path traversal, and partial files;
|
||||||
|
- atomic creation and revocation under concurrent reads;
|
||||||
|
- clean failure when the registry or socket is unavailable;
|
||||||
|
- absence of credential values and digests in human output, JSON, errors, and logs;
|
||||||
|
- Nginx `auth_request` integration against a synthetic upstream and `/rpc/ping` fixture;
|
||||||
|
- `401` for credential failures and `503` for infrastructure failures;
|
||||||
|
- unchanged successful request path and response body;
|
||||||
|
- independent service lifecycle from the ThothII stack.
|
||||||
|
|
||||||
|
Portability regression checks prove that:
|
||||||
|
|
||||||
|
- the existing REST connector still sends the same `X-API-Key` header;
|
||||||
|
- `postgres_direct` and `ssh_tunnel` configuration remain unchanged;
|
||||||
|
- canonical local and server ThothII Compose rendering does not acquire `dwh-auth`;
|
||||||
|
- macOS and Windows installation contracts do not require the new binary or files;
|
||||||
|
- existing `tht`, backend, frontend, and harness gates remain green in proportion to the touched
|
||||||
|
source boundaries.
|
||||||
|
|
||||||
|
There is no separate PSD test environment. Local synthetic and integration tests precede a
|
||||||
|
bounded, reversible production rollout using only `/rpc/ping`. The absence of a test environment
|
||||||
|
does not trigger preservation work for legacy test sessions.
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
Implementation is incomplete until the following clear, secret-free documents exist and pass
|
||||||
|
their documentation checks:
|
||||||
|
|
||||||
|
1. a generic server installation and administration guide for `dwh-auth`;
|
||||||
|
2. a generic enrollment guide for issuing a key to a macOS, Windows, Linux, or headless ThothII
|
||||||
|
installation;
|
||||||
|
3. exact GUI and `API_KEY_FILE` configuration paths;
|
||||||
|
4. a TLS guide covering `TLS_CA_FILE`, trusted delivery, fingerprint verification, renewal, and
|
||||||
|
coordinated client updates;
|
||||||
|
5. a PSD runbook for backup, installation, legacy import, dual-key activation, Mac update,
|
||||||
|
observation, revocation, negative testing, and rollback;
|
||||||
|
6. a troubleshooting table for `401`, `503`, TLS failures, revoked keys, and unreadable registry
|
||||||
|
records;
|
||||||
|
7. sanitized evidence templates that never request secret values or digests;
|
||||||
|
8. links from the existing local, server, workspace, and final unified user manuals.
|
||||||
|
|
||||||
|
Examples use synthetic hostnames, IDs, fingerprints, and credentials. No real key, certificate
|
||||||
|
body, password, connection string, or clinical result is committed.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
The design is implemented only when:
|
||||||
|
|
||||||
|
- each REST installation can be identified and revoked independently;
|
||||||
|
- no plaintext server credential is stored in the registry;
|
||||||
|
- the portable ThothII connector contract is unchanged;
|
||||||
|
- the PSD ThothII server continues to select `postgres_direct`;
|
||||||
|
- stopping ThothII does not stop `dwh-auth` or `/dwh/` authentication;
|
||||||
|
- the authenticator fails closed and its logs are sanitized;
|
||||||
|
- the dual-key production procedure proves both positive and negative outcomes;
|
||||||
|
- the exposed legacy credential is demonstrably rejected;
|
||||||
|
- the current TLS limitations are documented precisely rather than silently bypassed;
|
||||||
|
- database read-only authorization is independently verified;
|
||||||
|
- all documentation deliverables are reviewed and reproducible by an operator who did not write
|
||||||
|
the implementation.
|
||||||
|
|
||||||
|
## Authorization gates
|
||||||
|
|
||||||
|
Approval of this design authorizes documentation and planning only. It does not authorize:
|
||||||
|
|
||||||
|
- reading or importing the legacy credential;
|
||||||
|
- creating real keys;
|
||||||
|
- installing a binary or `systemd` unit;
|
||||||
|
- writing the registry directory;
|
||||||
|
- modifying or reloading Nginx;
|
||||||
|
- querying or changing PostgreSQL;
|
||||||
|
- changing, stopping, or removing the legacy ThothII stack.
|
||||||
|
|
||||||
|
Those actions require the executable plan, the remaining survey gates, explicit mutation
|
||||||
|
authorization, and retained sanitized evidence.
|
||||||
Reference in New Issue
Block a user