# Installazione Docker nei quattro contesti operativi Questa guida descrive l'installazione di ThothII usando le due immagini applicative: - `thothii-core`: backend Fastify, harness `tht` e Pi; - `thothii-frontend`: frontend React servito da nginx. I database e i servizi di embedding restano esterni, salvo il profilo opzionale `local-vector`, che avvia un PostgreSQL/pgvector nello stesso progetto Compose. ## Prerequisiti comuni Installare Docker Engine/Compose v2 sul server oppure Docker Desktop su macOS/Windows. La macchina deve poter raggiungere: - il DWH/DWH REST, se usato dal workspace; - il vector DB REST o pgvector; - il servizio di embedding (Ollama o endpoint compatibile); - il provider del modello Pi, se si usano provider hosted. Clonare il repository e lavorare dalla sua radice: ```sh git clone ThothII cd ThothII cp deploy/env.example deploy/.env mkdir -p deploy/secrets deploy/workspaces chmod 600 deploy/.env ``` La configurazione installativa rimane tutta dentro `ThothII/`: ```text ThothII/ ├── deploy/.env # riferimenti ai file e valori non riservati ├── deploy/secrets/ # file secret locali, esclusi da Git └── deploy/workspaces/ # workspace YAML senza password/token ``` I soli file da creare o modificare dopo il clone sono questi: | File interno a `ThothII/` | Operazione | Contenuto | |---|---|---| | `deploy/.env` | copiare da `deploy/env.example` e modificare | endpoint, nomi, profili e percorsi relativi dei secret | | `deploy/secrets/dwh-api-key` | creare | una API key DWH, una riga/valore | | `deploy/secrets/dwh_password` | creare solo con `postgres_direct` | password dell'utente DWH diretto | | `deploy/secrets/vector-reader-api-key` | creare | chiave reader vector REST, se usata | | `deploy/secrets/vector-writer-api-key` | creare | chiave writer vector REST, se usata | | `deploy/secrets/model-api-key` | creare | chiave del provider Pi a chiave singola | | `deploy/secrets/ca-chain.pem` | creare, se serve TLS privato | catena CA PEM pubblica | | `deploy/secrets/vector_bootstrap_password` | creare con `local-vector` | password bootstrap pgvector | | `deploy/secrets/vector_migrator_password` | creare con `local-vector` | password migrator | | `deploy/secrets/vector_reader_password` | creare con `local-vector` | password reader pgvector | | `deploy/secrets/vector_writer_password` | creare con `local-vector` | password writer pgvector | | `deploy/workspaces/.yaml` | copiare/modificare | adapter, endpoint non riservati, `roots` e sorgente Evidence | Non occorre creare file in `/etc`, `/opt`, `~/.thothii` o in altre directory esterne per seguire questa guida. I file secret sono ignorati da Git tramite `.gitignore`, ma restano disponibili a Docker perché Compose li legge dal progetto locale. I secret non devono essere inseriti in `deploy/.env`, nei file workspace o negli URL. `deploy/.env` contiene solo host, porte, nomi di database e percorsi relativi come `THT_DWH_API_KEY_SECRET_FILE=deploy/secrets/dwh-api-key`. Un workspace YAML deve contenere al massimo un riferimento come `password_file`, mai la password stessa; gli URL non devono contenere user, password o token. Dopo aver modificato `deploy/.env`, caricarlo nella shell da cui si eseguono i comandi Docker: ```sh set -a . ./deploy/.env set +a ``` Le tre righe fanno questo: 1. `set -a` dice alla shell di esportare automaticamente tutte le variabili assegnate da questo momento in poi; 2. `. ./deploy/.env` (il punto iniziale è l'abbreviazione di `source`) legge ed esegue il file nella shell corrente. Le variabili diventano quindi disponibili a `docker compose` e ai processi figli, senza aprire una nuova shell; 3. `set +a` disattiva l'esportazione automatica per evitare che assegnazioni successive vengano esportate accidentalmente. Esempio: se `deploy/.env` contiene ```dotenv THT_MODEL_API_KEY_SECRET_FILE=deploy/secrets/model-api-key ``` dopo il comando `source` la shell possiede `THT_MODEL_API_KEY_SECRET_FILE` e Compose può usare quel percorso per leggere il secret file. Il valore della API key non viene caricato in `deploy/.env`: resta nel file separato sotto `deploy/secrets/`. La forma equivalente, senza modificare l'ambiente della shell, è passare il file direttamente a Compose: ```sh docker compose --env-file deploy/.env -f compose.yaml -f deploy/compose.production.yaml \ --profile external up --build --wait ``` Non usare `source`/`.` con file ricevuti da terzi senza averne verificato il contenuto: un file `.env` sourced è codice shell, non un semplice formato dati. In questa guida il file viene creato localmente da `deploy/env.example` e contiene solo assegnazioni di configurazione e percorsi. Per i comandi successivi si può quindi scegliere una sola delle due modalità: fare `source` una volta all'inizio della sessione, oppure aggiungere `--env-file deploy/.env` a ogni comando `docker compose`. Salvare invece ogni credenziale in un file separato dentro `ThothII/deploy/secrets/`. Sul sistema host il file deve essere leggibile solo dall'utente/servizio che esegue Docker (`0400` se solo lettura, `0600` se l'operatore deve poterlo aggiornare). Compose lo monta poi nel container come secret read-only sotto `/run/secrets`. Il `0444` osservabile dentro il container è normale per il mount dei secret Docker e non rende il file host pubblico: il file host resta protetto e il container riceve una copia/mount temporaneo non scrivibile. Il modello Pi usa `THT_MODEL_API_KEY_SECRET_FILE` per i provider autenticati con una singola chiave. Provider composti (Bedrock, Azure OpenAI Responses, Cloudflare Workers AI/Gateway) sono rifiutati esplicitamente: richiedono un bundle di credenziali non rappresentabile da un solo file. ## 1. Server applicativo insieme a DWH e vector DB Questo profilo è adatto quando ThothII, il database relazionale e il vector DB sono nella stessa rete/server, ma i database non devono necessariamente essere containerizzati da ThothII. Si usa il profilo `external`; gli endpoint possono essere nomi DNS, IP privati o nomi di servizio della rete Docker. ### Configurazione Prima si definiscono in `ThothII/deploy/.env` i percorsi host nelle variabili `THT_*_SECRET_FILE`: ```dotenv THT_DWH_API_KEY_SECRET_FILE=deploy/secrets/dwh-api-key THT_VEC_API_KEY_SECRET_FILE=deploy/secrets/vector-reader-api-key THT_VEC_WRITE_API_KEY_SECRET_FILE=deploy/secrets/vector-writer-api-key THT_MODEL_API_KEY_SECRET_FILE=deploy/secrets/model-api-key THT_CA_SECRET_FILE=deploy/secrets/ca-chain.pem ``` Queste righe vanno scritte nel file interno `ThothII/deploy/.env`, non eseguite solo temporaneamente nella shell. Nella forma documentale, i cinque target sono indicati come ``, ``, ``, `` e ``. Le parentesi angolari sono segnaposto e **non** vanno digitate nella shell: nei comandi eseguibili si usano le variabili tra virgolette doppie: ```sh for secret_file in \ "$THT_DWH_API_KEY_SECRET_FILE" "$THT_VEC_API_KEY_SECRET_FILE" \ "$THT_VEC_WRITE_API_KEY_SECRET_FILE" "$THT_MODEL_API_KEY_SECRET_FILE" "$THT_CA_SECRET_FILE"; do sudo install -d -m 700 "$(dirname "$secret_file")" done sudo install -m 600 /dev/null "$THT_DWH_API_KEY_SECRET_FILE" sudo install -m 600 /dev/null "$THT_VEC_API_KEY_SECRET_FILE" sudo install -m 600 /dev/null "$THT_VEC_WRITE_API_KEY_SECRET_FILE" sudo install -m 600 /dev/null "$THT_MODEL_API_KEY_SECRET_FILE" sudo install -m 600 /dev/null "$THT_CA_SECRET_FILE" ``` Così ogni `sudo install` usa la variabile corretta e non si rischia di creare il file in un percorso diverso da quello che Compose leggerà. Se i secret sono forniti da un secret manager, si impostano le stesse variabili ai file materializzati dal manager e si saltano i comandi `install`. `install` è un comando Unix per creare/copiare un file impostando contestualmente i permessi. In `install -m 600 /dev/null DEST`: - `/dev/null` è una sorgente vuota, quindi il comando crea (o sostituisce) `DEST` senza inserire una password; - `-m 600` imposta i permessi `rw-------` (lettura/scrittura solo per il proprietario); - `DEST` è il file che l'operatore deve poi riempire con il secret. Dopo la creazione, scrivere il contenuto in ogni target (l'esempio seguente usa un file sorgente protetto; ripeterlo per le altre variabili): ```sh sudo sh -c 'umask 077; cat > "$1"' sh "$THT_DWH_API_KEY_SECRET_FILE" \ < /percorso/protetto/dwh-api-key ``` Il percorso `deploy/secrets/` è la directory interna al clone usata da questa guida: il software non cerca automaticamente le API key in `/etc` né in una directory speciale. Il percorso host è quello indicato nelle variabili `THT_*_SECRET_FILE`; Compose legge quel file e lo monta nel container al percorso interno dichiarato dal servizio, normalmente `/run/secrets/dwh_api_key`, `/run/secrets/vector_reader_api_key` o `/run/secrets/model_api_key`. Il comando non è un gestore di password e non va usato per stampare il secret sulla riga di comando. Su Windows è preferibile usare WSL2 per creare il file con permessi Unix, oppure creare un file locale ACL-protetto tramite uno strumento aziendale di gestione dei secret. Non usare `ConvertFrom-SecureString` come contenuto del file: produce una rappresentazione cifrata che ThothII non può usare come API key. ```sh # Dentro WSL2, dalla directory radice del clone ThothII. mkdir -p deploy/secrets umask 077 read -r -s secret; printf '%s' "$secret" > deploy/secrets/dwh-api-key unset secret ``` Per i secret usati da Docker Desktop è preferibile una directory locale non sincronizzata e accessibile a Docker Desktop; non usare una cartella Git o OneDrive condivisa. In alternativa, creare i file dentro WSL2 con `umask 077` e passare a Compose il path Windows risultante. Impostare gli endpoint e i riferimenti ai secret nel servizio core (preferibilmente tramite un file `.env` gestito dall'operatore, non committato): ```sh export THT_DB_NAME=warehouse export THT_DWH_REST_URL=https://dwh.internal.example export THT_VEC_REST_URL=https://vectors.internal.example export THT_OLLAMA_URL=https://embeddings.internal.example export AUTH_MODE=none export THOTH_PUBLIC_EXPOSURE=false ``` Queste variabili con suffisso `_SECRET_FILE` sono input di Compose sul server host. Non vanno confuse con le variabili `_FILE` viste dal processo dentro il container: ad esempio `THT_MODEL_API_KEY_SECRET_FILE=deploy/secrets/model-api-key` viene trasformata dal file `deploy/compose.production.yaml` in `THT_MODEL_API_KEY_FILE=/run/secrets/model_api_key`. Il backend legge quindi `/run/secrets/model_api_key`, non il file host sotto `deploy/secrets`. Preparare un file YAML in `deploy/workspaces/`. Il workspace è la configurazione logica di una installazione: seleziona gli adapter, gli endpoint non riservati e le radici persistenti. Per esempio, con DWH REST e vector DB HTTP: ```yaml language: en dwh: type: thoth_rest database: {database: warehouse, schema: datawarehouse} endpoint: {base_url: https://dwh.internal.example} vectors: type: thoth_vector_http reader: {base_url: https://vectors.internal.example} writer: {base_url: https://vectors.internal.example} roots: {artifacts: artifacts, indexes: indexes, sessions: sessions} evidence: {source_root: /data/source, evidence_dir: evidence} embeddings: {base_url: https://embeddings.internal.example, model: nomodel, dim: 768} ``` Con DWH PostgreSQL e pgvector raggiungibili direttamente dalla rete Docker: ```yaml language: en dwh: type: postgres_direct connection: {host: dwh.internal.example, database: warehouse, schema: public, user: thoth_reader, password_file: /run/secrets/dwh_password} vectors: type: pgvector_direct reader: {host: vector.internal.example, database: thoth, schema: vectors, user: thoth_vector_reader, password_file: /run/secrets/vector_reader_password} writer: {host: vector.internal.example, database: thoth, schema: vectors, user: thoth_vector_writer, password_file: /run/secrets/vector_writer_password} roots: {artifacts: artifacts, indexes: indexes, sessions: sessions} ``` I nomi dei `type` sono contratti applicativi, non descrizioni libere: usare quelli esposti da `tht doctor` e dagli esempi del repository (`thoth_rest`, `thoth_vector_http`, `postgres_direct`, `pgvector_direct`). `dwh.type` sceglie come interrogare il DWH; `vectors.type` sceglie come leggere/scrivere il vector DB. Cambiare questi valori può richiedere anche campi specifici dell'adapter e secret file coerenti. `roots` contiene percorsi logici, non percorsi host arbitrari. Con `THT_DATA_ROOT=/data`, `artifacts: artifacts` diventa `/data/workspaces//artifacts` (e analogamente per `indexes` e `sessions`), evitando che un YAML possa scrivere fuori dal volume applicativo. Un path host va esposto esplicitamente con un bind mount read-only/read-write nel Compose e poi referenziato dal workspace secondo le regole di sicurezza; non inserire `/Users/...` o `C:\\...` direttamente in un workspace destinato a più sistemi operativi. ### Avvio e verifica ```sh docker compose -f compose.yaml -f deploy/compose.production.yaml \ --profile external up --build --wait docker compose -f compose.yaml -f deploy/compose.production.yaml \ --profile external exec core /opt/venv/bin/tht doctor --json ./scripts/docker-smoke.sh ``` Il frontend è pubblicato su `127.0.0.1:8080` per default. Per esposizione pubblica usare un reverse proxy autenticato TLS; non impostare `THOTH_PUBLIC_EXPOSURE=true` senza `AUTH_MODE=upstream` e senza il proxy che inietta `X-Authenticated-User`. ## 2. Mac locale con DWH esterno e vector DB locale Questo è il profilo consigliato per un Mac Apple Silicon o Intel: Docker Desktop ospita pgvector e ThothII, mentre DWH ed embeddings possono essere remoti. Ollama eseguito sul Mac è raggiungibile dai container con `host.docker.internal`. Creare quattro password locali dentro `ThothII/deploy/secrets/` (la directory è esclusa da Git): ```sh mkdir -p deploy/secrets for name in bootstrap migrator reader writer; do umask 077; openssl rand -base64 32 >"deploy/secrets/$name" done export THT_VECTOR_BOOTSTRAP_PASSWORD_SECRET_FILE=deploy/secrets/bootstrap export THT_VECTOR_MIGRATOR_PASSWORD_SECRET_FILE=deploy/secrets/migrator export THT_VECTOR_READER_PASSWORD_SECRET_FILE=deploy/secrets/reader export THT_VECTOR_WRITER_PASSWORD_SECRET_FILE=deploy/secrets/writer export THT_OLLAMA_URL=http://host.docker.internal:11434 ``` Avviare il profilo: ```sh docker compose -f compose.yaml -f deploy/compose.local-vector.yaml \ --profile local-vector up --build --wait ``` Per eseguire i job di preprocessing usare anche l'overlay che abilita la catena health-check → reconcile → migrate: ```sh docker compose -f compose.yaml -f deploy/compose.local-vector.yaml \ -f deploy/compose.preprocess.yaml -f deploy/compose.preprocess-local-vector.yaml \ --profile local-vector --profile preprocess build preprocess-evidence docker compose -f compose.yaml -f deploy/compose.local-vector.yaml \ -f deploy/compose.preprocess.yaml -f deploy/compose.preprocess-local-vector.yaml \ --profile local-vector --profile preprocess run --rm preprocess-evidence ``` Montare i testi Evidence in `/data/source/evidence` tramite il workspace o un override Compose. Il volume `thoth_data` contiene sessioni, artefatti e corpus; `vector_data` contiene solo pgvector. Per verificare rotazione credenziali e recovery: ```sh ./scripts/local-vector-smoke.sh ./scripts/local-vector-smoke.sh --backup-restore ``` ## 3. PC Windows locale Usare Docker Desktop con backend WSL2, abilitare l'integrazione con la distribuzione WSL e conservare il repository in un percorso condiviso con Docker. È preferibile lavorare da PowerShell nella directory del progetto. ```powershell $secretDir = Join-Path (Get-Location) "deploy\secrets" New-Item -ItemType Directory -Force $secretDir | Out-Null foreach ($name in @("bootstrap", "migrator", "reader", "writer")) { $bytes = New-Object byte[] 32 [Security.Cryptography.RandomNumberGenerator]::Fill($bytes) [Convert]::ToBase64String($bytes) | Set-Content -NoNewline (Join-Path $secretDir $name) } $env:THT_VECTOR_BOOTSTRAP_PASSWORD_SECRET_FILE = Join-Path $secretDir "bootstrap" $env:THT_VECTOR_MIGRATOR_PASSWORD_SECRET_FILE = Join-Path $secretDir "migrator" $env:THT_VECTOR_READER_PASSWORD_SECRET_FILE = Join-Path $secretDir "reader" $env:THT_VECTOR_WRITER_PASSWORD_SECRET_FILE = Join-Path $secretDir "writer" $env:THT_OLLAMA_URL = "http://host.docker.internal:11434" ``` Da PowerShell, i path assoluti vengono passati a Compose tramite le variabili precedenti; non scrivere password direttamente nello YAML. Avviare lo stesso profilo del Mac: ```powershell docker compose -f compose.yaml -f deploy/compose.local-vector.yaml ` --profile local-vector up --build --wait docker compose -f compose.yaml -f deploy/compose.local-vector.yaml ` -f deploy/compose.preprocess.yaml -f deploy/compose.preprocess-local-vector.yaml ` --profile local-vector --profile preprocess run --rm preprocess-evidence ``` Se Docker Desktop segnala un bind mount non condiviso, aggiungere la directory del repository in Docker Desktop → Settings → Resources → File Sharing. Se Ollama gira su Windows, usare `host.docker.internal`; se gira in WSL2, usare l'endpoint raggiungibile dalla rete Docker. Verificare il profilo con: ```powershell docker compose -f compose.yaml -f deploy/compose.local-vector.yaml --profile local-vector config docker compose -f compose.yaml -f deploy/compose.local-vector.yaml --profile local-vector ps ``` ## 4. Server applicativo distinto da DB e Evidence Questo profilo separa il server Docker di ThothII dal DWH, vector DB e repository Evidence. Usare `compose.production.yaml` e configurare gli adapter con endpoint raggiungibili dal server applicativo. Le Evidence non devono essere copiate nel container se sono già accessibili via HTTP/S3 o tramite un filesystem montato dal sistema operativo. Configurare firewall e DNS in modo che il server applicativo possa raggiungere solo gli endpoint necessari. Esempi di sorgente Evidence: - `filesystem`: NFS/SMB montato sul server e passato come root read-only al workspace; - `http`: URL HTTPS con allowlist/limiti SSRF e cache condizionata; - `s3`: bucket/prefix con secret references e endpoint custom solo con trust boundary esplicito. Impostare `THT_DOCS_ROOT`/il workspace per il path montato oppure configurare il tipo HTTP/S3, poi avviare: ```sh docker compose -f compose.yaml -f deploy/compose.production.yaml \ --profile external config --quiet docker compose -f compose.yaml -f deploy/compose.production.yaml \ --profile external up --build --wait docker compose -f compose.yaml -f deploy/compose.production.yaml \ --profile external exec core /opt/venv/bin/tht doctor --json ``` Per un vector DB remoto usare le chiavi reader/writer distinte quando il servizio lo consente; per un DWH REST usare `THT_DWH_REST_URL` e il relativo secret, senza introdurre connessioni dirette nel container. Il preprocessing DWH/Evidence può essere eseguito sullo stesso server applicativo con un volume `/data` persistente, mantenendo separati i lock e gli artefatti dei job. ## Controlli comuni post-installazione 1. `docker compose ... config --quiet` non deve mostrare password o token in chiaro. 2. `docker compose ... ps` deve mostrare `core` e `frontend` healthy. 3. `tht doctor --json` deve restituire JSON valido; errori di dipendenza devono restare diagnostici e non esporre secret. 4. Eseguire lo smoke coerente col profilo (`docker-smoke.sh`, `preprocess-smoke.sh` o `local-vector-smoke.sh`). 5. Conservare il volume `thoth_data` e i backup pgvector fuori dal ciclo di deploy; usare `down --volumes` solo per ambienti effimeri o dopo aver verificato il backup. ## Risoluzione rapida dei problemi - **Core healthy ma nessun modello:** controllare `THT_MODEL_API_KEY_SECRET_FILE`, il provider selezionato e che non sia uno dei provider composti non supportati. - **Preprocess fallisce subito:** verificare l'overlay `compose.preprocess-local-vector.yaml`, i quattro secret pgvector e che il mount Evidence sia leggibile. - **Vector health non disponibile:** controllare `vector-db`, `vector-reconcile`, `vector-migrate` e le password reader/writer; non usare la password bootstrap nelle query applicative. - **Evidence non trovate:** controllare il path nel workspace, la rete HTTP/S3 e i limiti di dimensione/paginazione; verificare che il corpus ACTIVE appartenga allo stesso workspace.