Files
ThothII/docs/operations/server-upgrade-gitea-workspace-v2.md
T

32 KiB

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 e rilascio coordinato dei due repository; 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 le modifiche al portale Omics seguire prima la consegna via GitHub e server verso Gitea PSD. L'operatore lavora in /home/chirone/omics_portal: fetch del branch dedicato da GitHub, verifica SHA, push esplicito a Gitea. Il trasferimento non modifica il checkout di produzione e non autorizza un deploy; non cercare credenziali Gitea PSD sul Mac per aggirare questa procedura concordata.
  • 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:

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:

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:

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:

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:

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:

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:

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:

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.

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/<installation-id>/thothii-installation.yaml, e usarlo esplicitamente in ogni comando successivo. Aggiungere all'operator.env generato, usando un editor che non stampi i valori:

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:

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 <PUBLIC_URL>/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:

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:

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

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:

"${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à.

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:

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:

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:

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:

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:

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:

    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:

    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