# Upgrade del server alla configurazione ThothII v2 Questo runbook è il passaggio di consegne per il Codex che opererà sul server remoto. Porta una installazione precedente alla configurazione corrente di ThothII senza modificare Authentik o il DWH esterno e senza cancellare lo stack precedente durante il primo cutover. ## Scelta preliminare: server autonomo oppure Omics I passaggi di questo runbook che configurano OIDC diretto, gruppi e `authentication.runtimeProjection` riguardano **ThothII autonomo**, da rendere con `shell.mode: full`. Non applicarli all'integrazione Datamart Builder: Omics usa **embedded/upstream** e mantiene il proprio accesso Authentik. Il core riceve l'identità verificata dal proxy senza un secondo login né un secondo auth.yaml. Prima dell'inventario identificare quale percorso è approvato. Per Omics seguire [autenticazione upstream](../install/authentication-upstream.md) e [rilascio coordinato dei due repository](shell-and-localization.md#preparare-il-rilascio-coordinato); i gate di backup, isolamento, catalogo, storage e rollback di questo runbook rimangono validi, ma non copiare i passi auth del percorso autonomo. Il passaggio da issuer `portal` a un issuer OIDC differente non trasferisce automaticamente la proprietà delle sessioni. La procedura si applica a `main` quando contiene almeno il commit `eba6148511675fc6a187aabb69a975adc5e3c542`. Deve essere presente anche questo file. Il commit minimo è un controllo di sicurezza, non un invito a fermarsi a quella revisione: installare sempre la `origin/main` approvata dall'operatore. ## Regole non negoziabili - Per il rilascio Omics seguire la [consegna corrente a Codex sul server](server-codex-handoff.md). Acquisire il branch dedicato da GitHub, verificare SHA e integrarlo con i progressi del server; nessuna replica del repository è richiesta. - Eseguire prima l'intero inventario in sola lettura e consegnarlo all'operatore. - Non stampare mai password, token, chiavi private, cookie, file `.env` o contenuti dei Docker secret. Nei report sono ammessi solo percorsi, nomi delle variabili e valori non segreti. - Non usare `set -x`, credenziali dentro URL Git, `http.sslVerify=false`, `docker compose down -v`, `docker volume rm`, `git reset --hard` o pulizie ricorsive. - Non inventare l'indirizzo IP di `git.tylconsulting.it` e non aggiungerlo a `/etc/hosts` senza un valore e un fingerprint TLS forniti dall'amministratore. - Non sovrascrivere la vecchia configurazione. Creare una nuova installation ID e mantenere configurazione, immagini, container e volumi precedenti fino all'accettazione scritta. - Fermarsi a ogni **Gate operatore**. Il Codex prepara evidenze e comandi esatti; l'operatore autorizza backup con segreti, fermo dello stack, cambio del reverse proxy e rimozioni. - Il DWH resta read-only e diretto. Nessuna migrazione o DDL deve essere eseguita sul DWH. ## Cosa contiene la nuova release Il confine informale “da auth in poi” non coincide con un singolo commit Git. OIDC/Authentik e il CLI host `tht` fanno parte del contratto che il nuovo server deve rispettare, ma la tranche promossa da `f5861526` a `eba61485` riguarda soprattutto: - Database Management e il catalogo metadati isolato nel PostgreSQL interno `catalog-db`; - sincronizzazione di tabelle, colonne e relazioni, generazione descrizioni e flag di sensibilità; - Installation Model Catalog schema v2 come unica sorgente authored per modelli e provider; - workspace schema v4 e proiezioni runtime generate; - Qdrant per gli indici del workspace e Ollama per il solo modello di embedding; - analisi locale deterministica delle colonne sensibili, con NER CPU opzionale; - hardening delle proiezioni e stabilizzazione dei gate di test pre-deploy. La topologia base attesa è: | Servizio | Funzione | Persistenza | | --- | --- | --- | | `frontend` | UI React | nessuna persistenza propria | | `core` | Fastify, Pi RPC e catalog API | volumi settings, Pi, registry, secrets e sessioni | | `catalog-db` | PostgreSQL del catalogo metadati | volume `catalog-data` | | `catalog-migrate` | migrazioni esplicite del catalogo | job one-shot, deve terminare con exit 0 | | `qdrant` | indice vettoriale workspace | volume `qdrant-data` | | `embedding` | Ollama interno per embedding | volume `embedding-models` | | `embedding-model-init` | scarica/verifica il modello | job one-shot, deve terminare con exit 0 | `catalog-db` non è il DWH. Non pubblica porte sull'host e usa credenziali runtime e migrator separate. Ollama non controlla le password utente: nel percorso autonomo vale OIDC tramite Authentik; nel percorso embedded Omics verifica l'accesso e il core usa upstream. Il PostgreSQL per le **sessioni** è un'altra funzione ancora. L'overlay `deploy/compose.session-server.yaml.example` è esplicitamente opt-in: non abilitarlo durante questo upgrade salvo decisione separata dell'operatore e presenza di un database sessioni già provisionato. L'introduzione richiesta qui è soltanto `catalog-db`. ## Stato finale desiderato Sono due repository distinti e non devono essere confusi: | Uso | Remote atteso | Branch | Trasporto | | --- | --- | --- | --- | | codice applicativo ThothII, remote `origin` | `https://git.tylconsulting.it/mptyl/ThothII.git` | `main` | HTTPS | | workspace PSD, `workspaceRepository` | `git@github.com:mptyl/tht-workspace-psd.git` | `main` | SSH | Il riferimento locale approvato usa il workspace precedente e questi default di modello: - sessione: `zai/glm-5.3`; - generazione metadati: `zai/glm-5.3`; - embedding: `ollama/qwen3-embedding:0.6b`, 1024 dimensioni. Non copiare i file generati locali. La sorgente authored è il nuovo `thothii-installation.yaml`; i file Pi, settings, catalogo runtime e Compose derivati vengono rigenerati da `tht`. ## 0. Preparare le variabili di lavoro Usare percorsi assoluti reali. I valori seguenti sono esempi e vanno sostituiti dopo l'inventario: ```bash THTII_REPO=/absolute/path/to/ThothII THTII_GITEA_URL=https://git.tylconsulting.it/mptyl/ThothII.git THTII_MIN_COMMIT=eba6148511675fc6a187aabb69a975adc5e3c542 THTII_NEW_ID=psd-server-v2 THTII_WORKSPACE_REMOTE=git@github.com:mptyl/tht-workspace-psd.git THTII_WORKSPACE_BRANCH=main THTII_OLD_INSTALLATION=/absolute/path/to/legacy/thothii-installation.yaml THTII_NEW_INSTALLATION=/absolute/path/to/ThothII/deploy/psd-server-v2/thothii-installation.yaml THTII_NEW_ENV=/absolute/path/to/ThothII/deploy/psd-server-v2/operator.env THTII_BACKUP_ARCHIVE=/protected/backups/thothii-pre-v2-YYYYMMDD.zip THTII_SECRETS_FILE=/protected/thothii-v2/thothii.secrets THTII_PI_AUTH_FILE=/protected/thothii-v2/pi-auth.json THTII_CATALOG_RUNTIME_PASSWORD=/protected/thothii-v2/catalog-runtime-password THTII_CATALOG_MIGRATOR_PASSWORD=/protected/thothii-v2/catalog-migrator-password THTII_WORKSPACE_SSH_KEY=/protected/thothii-v2/workspace-deploy-key THTII_WORKSPACE_KNOWN_HOSTS=/protected/thothii-v2/workspace-known-hosts THTII_PUBLIC_URL=https://thothii.example.invalid THTII_AUTH_ISSUER=https://authentik.example.invalid/application/o/thothii/ THTII_AUTH_CLIENT_ID=replace-with-approved-client-id THTII_AUTHENTIK_URL=https://authentik.example.invalid ``` Definire inoltre, senza pubblicarne il contenuto: - percorso del descriptor e comando di avvio della vecchia installazione; - installation ID, Compose project, porte e reverse-proxy route correnti; - percorsi protetti per secret bundle, Pi auth, chiave SSH GitHub e `known_hosts`; - Public URL, issuer, client ID, URL Authentik e nomi dei gruppi; - host, porta, database, utente e CA del binding diretto DWH; - directory persistenti e directory backup della nuova installazione. **Esito richiesto:** tutte le variabili indicano target esistenti o percorsi nuovi approvati; nessun valore segreto compare nel terminale condiviso. ## 1. Inventario in sola lettura del server Non fare ancora fetch, checkout, stop o modifica di remote. Dal checkout esistente raccogliere: ```bash cd "$THTII_REPO" git status --short --branch git branch --show-current git rev-parse HEAD git remote -v git tag --points-at HEAD docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' docker volume ls df -h tht version --json ``` Se `tht` non esiste ancora, registrarlo come assente senza installarlo in questo passo. Individuare anche i container realmente appartenenti al vecchio stack, i loro image ID, i volumi montati e il comando esatto che oggi li avvia. Non assumere nomi dai file della nuova release. Raccogliere in forma redatta: 1. configurazione Authentik attuale: Public URL, issuer, client ID, callback, claim gruppi e gruppi ruolo; non leggere in output client secret o API token; 2. binding DWH diretto: tipo `postgres_direct`, endpoint, database e utente; non stampare password; 3. remote, branch e revisione del vecchio workspace; 4. stato delle sessioni e directory dati che devono essere conservate; 5. spazio disponibile per immagini, volume Ollama, Qdrant, catalogo e backup. Se il worktree è sporco o contiene configurazioni non committate, fermarsi: descrivere file e proprietario delle modifiche, senza fare stash, reset o checkout. **Esito richiesto:** un report redatto identifica vecchio stack, dati, immagini, rete, Authentik, DWH e workspace; il worktree è pulito oppure l'operatore ha risolto esplicitamente le differenze. ## 2. Rendere Gitea raggiungibile e assegnarlo a `origin` “Impostare Gitea come origin” e “risolvere il dominio Gitea” sono due operazioni diverse. Verificare prima DNS e TLS: ```bash getent hosts git.tylconsulting.it curl --fail --silent --show-error --head https://git.tylconsulting.it/ ``` Se `getent` non è disponibile, usare il resolver diagnostico già installato, per esempio `dig +short git.tylconsulting.it`. Se il nome non risolve, fermarsi e chiedere la correzione DNS o la mappatura IP ufficiale. Se TLS fallisce per una CA interna, installare la CA fornita dall'amministratore nel trust store; non disabilitare la verifica. Quando rete e TLS sono validi, registrare l'URL corrente di `origin` nel report e configurare Gitea: ```bash cd "$THTII_REPO" git remote get-url origin git remote set-url origin "$THTII_GITEA_URL" git remote -v git ls-remote --exit-code origin refs/heads/main ``` Se `origin` non esiste, usare `git remote add origin "$THTII_GITEA_URL"` al posto di `set-url`. Se serve autenticazione HTTPS, configurare il credential helper o il token nel secret store del server; non inserire il token nell'URL o nella shell history. **Esito richiesto:** `origin` mostra l'URL Gitea esatto per fetch e push e `ls-remote` restituisce `refs/heads/main`. Non eseguire push dal server. ## 3. Fare fetch e aggiornare `main` senza perdere il rollback Con worktree pulito: ```bash cd "$THTII_REPO" git fetch --prune origin git branch rollback/server-pre-v2-YYYYMMDD HEAD git switch main git merge --ff-only origin/main git merge-base --is-ancestor "$THTII_MIN_COMMIT" HEAD test -f docs/operations/server-upgrade-gitea-workspace-v2.md git status --short --branch git rev-parse HEAD ``` Sostituire `YYYYMMDD` con la data reale e registrare hash precedente, rollback branch e nuovo hash. Un merge non fast-forward, un file mancante o il fallimento di `merge-base` sono condizioni di stop. Non risolvere divergenze con reset o rebase sul server. **Esito richiesto:** `main` coincide con la `origin/main` approvata, contiene il commit minimo e il runbook, e il rollback branch punta ancora al vecchio HEAD. ## 4. Salvare vecchia installazione, dati e immagini ### Gate operatore A — custodia del backup Prima di leggere o archiviare secret, presentare l'elenco esatto dei file e volumi da salvare e ottenere l'autorizzazione. Il backup deve stare fuori dal repository, su storage protetto, con permessi restrittivi e checksum registrato separatamente. Se il vecchio CLI espone `backup` e accetta il suo descriptor, usare il percorso esplicito: ```bash umask 077 tht --installation "$THTII_OLD_INSTALLATION" backup \ --output "$THTII_BACKUP_ARCHIVE" --include-secrets --yes --drain sha256sum "$THTII_BACKUP_ARCHIVE" ``` Se il vecchio formato non è accettato, usare la procedura di backup già approvata sul server. Non improvvisare un archivio ricorsivo di `/srv` o della root. Il backup deve includere almeno: - descriptor, env non segreto, configurazione auth canonica, secret bundle, Pi auth e certificati; - repository/configurazione workspace precedente e sessioni persistite; - inventario di container, reti, volumi, mount, image name e image ID; - configurazione del reverse proxy necessaria a ripristinare il servizio. Conservare anche le immagini effettivamente in uso: assegnare tag di rollback agli image ID espliciti identificati al passo 1 oppure salvarli in un archivio Docker protetto. Non selezionare immagini tramite glob. Il nuovo build può riutilizzare gli stessi tag mentre i container vecchi continuano a funzionare. Il backup integrato va considerato sufficiente per un volume solo se il suo manifest lo elenca e la procedura di restore è stata verificata. In questa migrazione il nuovo `catalog-data` nasce vuoto; non esiste quindi un vecchio catalogo da importare. Per upgrade successivi, il catalogo PostgreSQL dovrà avere un dump consistente esplicito se il backup corrente non lo comprende. **Esito richiesto:** archivio, checksum, immagini di rollback e comando di ripristino sono verificati; nessun vecchio container o volume è stato eliminato. ## 5. Installare il CLI corrente e creare una installation v2 affiancata Dal nuovo checkout: ```bash cd "$THTII_REPO" ./scripts/install-tht.sh tht version --json ``` Preparare fuori dal repository file protetti distinti per: - secret bundle applicativo; - Pi auth; - password runtime e migrator di `catalog-db`; - chiave privata e `known_hosts` del repository workspace GitHub. Le due password del catalogo devono essere casuali, non vuote e diverse. I file devono essere leggibili solo dall'operatore autorizzato. Nel secret bundle servono i riferimenti approvati per i provider (`ZAI_API_KEY` e, se mantenuto, `DEEPSEEK_API_KEY`) e per Authentik (`THT_OIDC_CLIENT_SECRET`, `THT_AUTHENTIK_API_TOKEN`), mai i valori nel descriptor YAML. Verificare l'accesso SSH al workspace con la chiave e il file `known_hosts` approvati. Non costruire `known_hosts` accettando alla cieca il primo fingerprint restituito dalla rete: ```bash ssh-keygen -F github.com -f "$THTII_WORKSPACE_KNOWN_HOSTS" GIT_SSH_COMMAND="ssh -i $THTII_WORKSPACE_SSH_KEY -o IdentitiesOnly=yes -o UserKnownHostsFile=$THTII_WORKSPACE_KNOWN_HOSTS" \ git ls-remote --exit-code "$THTII_WORKSPACE_REMOTE" refs/heads/main ``` Creare la configurazione server in modalità `configure-only`; il profilo server richiede root per creare la proiezione auth con UID/GID del container. La versione corrente di `setup` non scrive ancora i due path catalogo nell'env generato: passarli all'ambiente del comando per validare Compose, quindi aggiungerli subito a `operator.env` nel passo successivo. ```bash cd "$THTII_REPO" sudo env \ THT_CATALOG_RUNTIME_PASSWORD_SOURCE="$THTII_CATALOG_RUNTIME_PASSWORD" \ THT_CATALOG_MIGRATOR_PASSWORD_SOURCE="$THTII_CATALOG_MIGRATOR_PASSWORD" \ tht setup --configure-only \ --installation-id "$THTII_NEW_ID" \ --profile server \ --workspace-remote "$THTII_WORKSPACE_REMOTE" \ --workspace-branch "$THTII_WORKSPACE_BRANCH" \ --workspace-access ssh \ --secrets-file "$THTII_SECRETS_FILE" \ --pi-auth-file "$THTII_PI_AUTH_FILE" \ --git-ssh-key-file "$THTII_WORKSPACE_SSH_KEY" \ --git-known-hosts-file "$THTII_WORKSPACE_KNOWN_HOSTS" \ --auth-mode oidc \ --auth-public-url "$THTII_PUBLIC_URL" \ --auth-issuer "$THTII_AUTH_ISSUER" \ --auth-client-id "$THTII_AUTH_CLIENT_ID" \ --auth-authentik-base-url "$THTII_AUTHENTIK_URL" \ --auth-user-group 'TOT Users' \ --auth-admin-group 'TOT Admin' ``` Se sul server i gruppi hanno nomi diversi, usare i nomi verificati al passo 1. Non indovinarli. Registrare il descriptor prodotto, normalmente `deploy//thothii-installation.yaml`, e usarlo esplicitamente in ogni comando successivo. Aggiungere all'`operator.env` generato, usando un editor che non stampi i valori: ```dotenv THT_CATALOG_RUNTIME_PASSWORD_SOURCE=/protected/thothii-v2/catalog-runtime-password THT_CATALOG_MIGRATOR_PASSWORD_SOURCE=/protected/thothii-v2/catalog-migrator-password THT_CATALOG_SYNC_TIMEOUT_MS=600000 ``` I valori a destra devono essere i percorsi assoluti reali preparati sopra. Verificare che `THTII_NEW_INSTALLATION` e `THTII_NEW_ENV` corrispondano ai file appena creati. **Esito richiesto:** esistono descriptor schema v2, env operatore e directory auth canonica/runtime della nuova installation; lo stack precedente è ancora in esecuzione. ## 6. Allineare il descriptor alla configurazione locale approvata Il `setup` genera un catalogo modelli valido ma generico. Sostituire il suo blocco `modelCatalog` con questo riferimento corrente, soltanto dopo aver verificato che il server raggiunga gli endpoint necessari: ```yaml modelCatalog: defaults: session: zai/glm-5.3 metadataGeneration: zai/glm-5.3 embedding: id: ollama/qwen3-embedding:0.6b dimensions: 1024 providers: deepseek: authentication: mode: pi_auth session: mode: pi_builtin models: deepseek-v4-pro: session: {} deepseek-v4-flash: session: {} deepseek-metadata: authentication: mode: secret_env apiKeyEnv: DEEPSEEK_API_KEY metadataGeneration: litellmProvider: deepseek models: deepseek-v4-pro: label: DeepSeek V4 Pro metadataGeneration: {} deepseek-v4-flash: label: DeepSeek V4 Flash metadataGeneration: {} zai: endpoint: baseUrl: https://api.z.ai/api/coding/paas/v4 authentication: mode: secret_env apiKeyEnv: ZAI_API_KEY session: mode: openai_compatible metadataGeneration: litellmProvider: openai models: glm-5.3: label: GLM 5.3 session: reasoning: true contextWindow: 200000 maxTokens: 131072 metadataGeneration: {} local-qwen: endpoint: baseUrl: https://ml-aritmolab.policlinicosandonato.it/v1 authentication: mode: none session: mode: openai_compatible metadataGeneration: litellmProvider: openai models: qwen3.6-35b-a3b: label: AritmoLab Qwen 3.6 35B A3B session: reasoning: false input: [text] cost: {input: 0, output: 0, cacheRead: 0, cacheWrite: 0} contextWindow: 131072 maxTokens: 16384 compatibility: supportsDeveloperRole: false supportsReasoningEffort: false supportsStore: false maxTokensField: max_tokens metadataGeneration: disableThinking: true ``` Controllare inoltre: - `profile: server` e `projectDirectory` uguale al checkout reale; - `workspaceRepository` uguale alla riga PSD della tabella iniziale; - `authentication.configDirectory` nella nuova directory canonica; - `authentication.runtimeProjection` in una directory separata, con `uid: 10001` e `gid: 10001`; - un solo override Git SSH; - nessun riferimento a vecchi `deploy/pi/models.json`, `deploy/pi/settings.json` o model metadata nel workspace; - nessun overlay session PostgreSQL salvo approvazione separata. La proiezione Compose dell'auth runtime è automatica: non aggiungere manualmente `deploy/compose.auth-runtime-projection.yaml` agli override. Se un endpoint di modello non è raggiungibile dal server, fermarsi e chiedere quale provider mantenere; non cambiare silenziosamente i default. Aggiornare l'env operatore usando `deploy/env/server.env.example` come checklist. Devono essere valorizzati almeno i percorsi `THT_CATALOG_RUNTIME_PASSWORD_SOURCE`, `THT_CATALOG_MIGRATOR_PASSWORD_SOURCE`, `THT_DATA_ROOT`, `THT_PI_STATE_ROOT`, `THT_WORKSPACE_REGISTRY_ROOT`, `THT_BACKUP_ROOT`, le sorgenti auth/Git e le porte. Le directory persistenti della nuova installation non devono coincidere accidentalmente con quelle legacy. **Esito richiesto:** descriptor e env contengono soltanto configurazione non segreta, puntano ai secret file protetti e descrivono esattamente workspace, auth e modelli approvati. ## 7. Verificare Authentik senza ricrearlo Se Public URL resta invariato, non creare una seconda applicazione/provider in Authentik. Il provider esistente deve avere: - callback esatta `/api/auth/oidc/callback`; - scope `openid profile email`; - claim `groups` non vuoto come array JSON; - gruppi applicativi attesi, normalmente `TOT Users` e `TOT Admin`; - token del service account con sola lettura dei gruppi. Configurare o rigenerare soltanto la configurazione canonica della nuova installation: ```bash sudo tht --installation "$THTII_NEW_INSTALLATION" auth configure \ --mode oidc \ --public-url "$THTII_PUBLIC_URL" \ --issuer "$THTII_AUTH_ISSUER" \ --client-id "$THTII_AUTH_CLIENT_ID" \ --authentik-base-url "$THTII_AUTHENTIK_URL" \ --user-group 'TOT Users' \ --admin-group 'TOT Admin' tht --installation "$THTII_NEW_INSTALLATION" auth status --json ``` Il comando non riceve secret come argomenti: li risolve dai riferimenti nel secret bundle. La nuova directory `auth-state` può partire vuota. Le vecchie sessioni browser non devono essere migrate e gli utenti dovranno autenticarsi di nuovo dopo il cutover. **Esito richiesto:** lo status redatto indica `mode: oidc`, URL e gruppi corretti; nessun secret è presente in YAML, argomenti, log o report. ## 8. Preflight e preparazione dei servizi interni `tht start` rigenera le proiezioni e avvia i servizi, ma non esegue il job esplicito `catalog-migrate`. Preparare quindi immagini e servizi interni con la stessa identità Compose della nuova installation mentre il vecchio frontend/core sono ancora attivi. Per prima cosa validare il descriptor e il render Compose: ```bash tht --installation "$THTII_NEW_INSTALLATION" update --check-only ``` Questo comando carica e valida il descriptor; dopo la modifica del `modelCatalog`, la proiezione generata da `setup` resta intenzionalmente precedente fino a `tht start`. Non usare `doctor` come gate in questo momento: i servizi sono fermi e il suo controllo di drift deve ancora fallire. Il gate completo viene eseguito al passo 10, dopo che `start` ha rigenerato la proiezione. Calcolare il Compose project name con la stessa regola del CLI e costruire l'array dei file. Questo blocco è esatto per il descriptor atteso, che ha il solo override Git SSH. Se nel descriptor ci sono altri override approvati, inserirli nello stesso ordine subito dopo `compose.git-ssh.yaml`; non ometterli e non aggiungere l'overlay sessioni per comodità. ```bash THTII_NEW_INSTALLATION=$(realpath "$THTII_NEW_INSTALLATION") THTII_INSTALL_DIR=$(dirname "$THTII_NEW_INSTALLATION") THTII_COMPOSE_PROJECT="thothii-$(printf '%s' "$THTII_NEW_INSTALLATION" | sha256sum | cut -c1-12)" THTII_COMPOSE=( docker compose --project-name "$THTII_COMPOSE_PROJECT" --project-directory "$THTII_REPO" --env-file "$THTII_NEW_ENV" -f "$THTII_REPO/compose.yaml" -f "$THTII_REPO/deploy/compose.server.yaml" -f "$THTII_REPO/deploy/compose.git-ssh.yaml" -f "$THTII_INSTALL_DIR/generated/compose.models.yaml" -f "$THTII_REPO/deploy/compose.auth-runtime-projection.yaml" ) "${THTII_COMPOSE[@]}" config --quiet ``` Non stampare il render completo. Costruire le immagini e avviare soltanto i servizi interni privi di porte host: ```bash "${THTII_COMPOSE[@]}" build core frontend "${THTII_COMPOSE[@]}" up --detach catalog-db qdrant embedding "${THTII_COMPOSE[@]}" run --rm catalog-migrate "${THTII_COMPOSE[@]}" run --rm embedding-model-init "${THTII_COMPOSE[@]}" ps --all ``` Il comando `catalog-migrate` deve terminare con exit 0 prima che `core` venga avviato. Anche `embedding-model-init` deve terminare con exit 0 e lasciare il modello nel volume della nuova installation. Questi comandi non pubblicano la nuova applicazione e non toccano i volumi legacy. Confermare inoltre che il server abbia accesso in uscita necessario al primo pull del modello Ollama e spazio sufficiente per `embedding-models`. Qdrant e `catalog-db` devono restare sulla rete Compose privata, senza porte host pubblicate. L'overlay NER `deploy/compose.sensitivity-ner.yaml` non è richiesto per il primo avvio. La policy deterministica funziona senza NER. Abilitare il modello CPU soltanto con la procedura e i gate in [Analisi locale della sensibilità](sensitivity-analysis.md). **Esito richiesto:** render Compose valido, immagini costruite, `catalog-db` healthy, migrazione exit 0, Qdrant/Ollama healthy e modello embedding inizializzato; `core` e `frontend` nuovi sono ancora fermi. ## 9. Cutover controllato ### Gate operatore B — finestra di manutenzione Presentare prima: - hash release, descriptor nuovo e risultato del preflight; - comando esatto per fermare e riavviare il vecchio stack; - comando di rollback, image ID conservati e durata stimata; - porte e route del reverse proxy che resteranno invariate o verranno cambiate. Dopo approvazione, bloccare nuove attività applicative e fermare solo lo stack legacy con il comando registrato al passo 1. Non rimuoverne container o volumi. Avviare quindi la nuova installation: ```bash tht --installation "$THTII_NEW_INSTALLATION" start ``` Il primo avvio deve: 1. rigenerare le proiezioni del model catalog aggiornato; 2. riutilizzare le immagini costruite e il catalogo già migrato al passo 8; 3. confermare healthy `catalog-db` e Qdrant; 4. avviare Ollama ed eseguire/verificare `embedding-model-init` per `qwen3-embedding:0.6b`; 5. avviare `core` soltanto dopo le dipendenze richieste; 6. avviare `frontend` sulla bind address prevista per il reverse proxy. Non interrompere il primo pull perché è lento se log e avanzamento restano regolari. In caso di errore, raccogliere soltanto i log sanitizzati: ```bash tht --installation "$THTII_NEW_INSTALLATION" status tht --installation "$THTII_NEW_INSTALLATION" logs ``` `embedding-model-init` è one-shot: `exited (0)` è lo stato corretto, non un guasto. Il job `catalog-migrate` è stato eseguito con `run --rm`, quindi fa fede l'exit 0 registrato al passo 8. Qualsiasi exit diverso da zero blocca il cutover. **Esito richiesto:** servizi persistenti healthy/running, job one-shot terminati con exit 0 e reverse proxy diretto alla nuova UI; i volumi legacy esistono ancora. ## 10. Verificare autenticazione e runtime Eseguire: ```bash tht --installation "$THTII_NEW_INSTALLATION" status tht --installation "$THTII_NEW_INSTALLATION" doctor --json tht --installation "$THTII_NEW_INSTALLATION" auth status --json tht --installation "$THTII_NEW_INSTALLATION" auth check tht --installation "$THTII_NEW_INSTALLATION" pi test ``` Dal browser verificare: 1. redirect ad Authentik e ritorno alla callback corretta; 2. login di un membro `TOT Users`; 3. accesso amministrativo soltanto a un membro `TOT Admin`; 4. logout e nuova autenticazione; 5. assenza di loop OIDC, cookie error o `502` del reverse proxy. È previsto che le sessioni browser precedenti non siano più valide. Non è previsto modificare issuer, client o gruppi per aggirare un errore: diagnosticare prima callback, claim e secret refs. **Esito richiesto:** `doctor`, `auth check` e `pi test` passano e i due ruoli Authentik sono mappati correttamente. ## 11. Ripuntare e materializzare il workspace PSD Il vecchio workspace runtime non è una sorgente da copiare. La nuova installation deve clonare e validare il repository Git approvato: ```text git@github.com:mptyl/tht-workspace-psd.git, branch main, access ssh ``` Nella UI amministrativa, nell'ordine: 1. usare **Update workspace repository** per fetch/clone e validazione; 2. selezionare il workspace PSD; l'ID atteso è `psd-clinical`, ma confermarlo dal catalogo `thoth-workspaces.yaml` anziché forzarlo; 3. usare **Validate workspace source**; 4. aprire **Database Management** e creare/aggiornare il database del workspace; 5. configurare il binding `postgres_direct` con gli stessi endpoint, ruolo read-only e CA già approvati sul server; reinserire la password nel secret store write-only, non nel repository; 6. eseguire **Test workspace connections**; 7. eseguire una sincronizzazione completa dello schema nel nuovo catalogo; 8. selezionare quel workspace come workspace globale dell'installation. Poi verificare e materializzare con il CLI: ```bash tht --installation "$THTII_NEW_INSTALLATION" workspace inspect \ --workspace psd-clinical --json tht --installation "$THTII_NEW_INSTALLATION" workspace preprocess run \ --workspace psd-clinical --json ``` Se il preprocessing fallisce, correggere il Catalog o la configurazione e rilanciare lo stesso comando dall'inizio: non esistono resume o rollback. Il comando completo materializza LSH e vettori di schema dal Metadata Catalog e ricostruisce la slice Evidence. Le nuove sessioni pinzano la revisione Git attiva del workspace. Non copiare nel nuovo descriptor i legacy `harness/workspaces/*.yaml` come catalogo authored e non trasferire vecchi indici vettoriali incompatibili. Il repository workspace schema v4 definisce identità ed Evidence; database, metadati, binding, segreti e selezione attiva restano locali all'installation. **Esito richiesto:** `workspace inspect` mostra repository, branch e revisione PSD corretti; test DWH diretto, schema sync e preprocess completo terminano con successo; Qdrant contiene la nuova generazione Evidence. ## 12. Accettazione finale Prima di dichiarare concluso l'upgrade, consegnare una checklist con evidenze redatte: - `git rev-parse HEAD` uguale alla release approvata e worktree pulito; - `origin` uguale al Gitea ThothII e workspace remote uguale al GitHub PSD; - `tht doctor --json`, `auth check` e `pi test` senza errori; - `catalog-db`, Qdrant, Ollama, core e frontend healthy; - `catalog-migrate` ed `embedding-model-init` terminati con exit 0; - login Authentik utente/admin verificato; - binding DWH diretto read-only verificato; - catalogo sincronizzato e preprocess workspace completato; - una nuova sessione di prova crea artifact e usa la revisione workspace attiva; - backup, checksum, rollback branch e immagini legacy ancora disponibili; - nessun secret aggiunto al repository o ai log condivisi. ### Gate operatore C — fine osservazione Non rimuovere ancora stack, immagini, volumi o backup legacy. La loro eliminazione è un'attività successiva e distruttiva, autorizzata soltanto dopo un periodo di osservazione e un backup completo della nuova installazione che includa esplicitamente anche il catalogo PostgreSQL. ## Rollback Eseguire il rollback se il nuovo stack non diventa healthy, Authentik non completa il login, il DWH diretto non passa il test o il workspace non si materializza entro la finestra approvata. 1. Raccogliere `status` e log sanitizzati della nuova installation. 2. Fermarla senza volumi: ```bash tht --installation "$THTII_NEW_INSTALLATION" stop ``` 3. Ripristinare i tag immagine legacy dagli image ID/tag conservati se il nuovo build li ha sostituiti. 4. Riportare il checkout al rollback branch soltanto se il comando legacy legge file dal checkout: ```bash cd "$THTII_REPO" git switch rollback/server-pre-v2-YYYYMMDD ``` 5. Riavviare il vecchio stack con il comando esatto registrato al passo 1. 6. Ripristinare la route reverse proxy precedente se era cambiata. 7. Verificare vecchia UI, Authentik e DWH; non montare i nuovi volumi nel vecchio stack. 8. Lasciare intatti i volumi della nuova installation per l'analisi post-mortem. **Esito richiesto:** il servizio precedente è nuovamente disponibile con i suoi dati originali e la nuova installazione è ferma ma ispezionabile. ## Riferimenti operativi - [Installazione e primo avvio](../install/first-start.md) - [Authentik](../install/authentik.md) - [OIDC generico](../install/authentication-oidc.md) - [Operazioni workspace](workspaces.md) - [Configurazione dei modelli Pi](../general/pi-configuration.md) - [Contesti Docker](compose-reference.md) - [Database Management](database-management.md) - [Analisi locale della sensibilità](sensitivity-analysis.md)