deploy: unify local and server compose stack
This commit is contained in:
+11
-38
@@ -1,42 +1,15 @@
|
||||
# ThothII Compose defaults. Copy this file to .env in the repository root.
|
||||
# The root .env is loaded automatically by Docker Compose; do not put secrets here.
|
||||
# Common non-secret Compose values. Select local.env or server.env with --env-file.
|
||||
# Run Compose with both files explicitly, for example:
|
||||
# docker compose -f compose.yaml -f deploy/compose.local.yaml up -d --build
|
||||
|
||||
COMPOSE_FILE=compose.yaml
|
||||
COMPOSE_PROFILES=
|
||||
THT_SECRETS_FILE=deploy/secrets/thothii.secrets
|
||||
|
||||
THOTH_HTTP_PORT=8080
|
||||
AUTH_MODE=none
|
||||
THOTH_PUBLIC_EXPOSURE=false
|
||||
MAX_PI_PROCESSES=4
|
||||
PI_PROVIDER=
|
||||
PI_MODEL=
|
||||
PI_THINKING=
|
||||
PI_AUTH_FILE=${HOME}/.pi/agent/auth.json
|
||||
|
||||
# Git-backed workspace registry. Set the remote only in the installation environment;
|
||||
# credentials and SSH/CA files remain outside this repository and are bind-mounted read-only.
|
||||
THT_WORKSPACE_REGISTRY_ROOT=/data/workspace-registry
|
||||
THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git
|
||||
THT_WORKSPACE_GIT_BRANCH=main
|
||||
THT_WORKSPACE_INSTALLATION_ID=local
|
||||
# THT_WORKSPACE_GIT_REMOTE=ssh://git@your-git-host/your-org/thoth-workspaces.git
|
||||
# THT_WORKSPACE_GIT_CREDENTIALS_FILE=/absolute/path/to/git-credentials
|
||||
# THT_WORKSPACE_GIT_CA_FILE=/absolute/path/to/git-ca.pem
|
||||
# THT_WORKSPACE_GIT_SSH_KEY_FILE=/absolute/path/to/git-ssh-key
|
||||
# THT_WORKSPACE_GIT_KNOWN_HOSTS_FILE=/absolute/path/to/git-known-hosts
|
||||
THT_WORKSPACE_GIT_AUTHOR_NAME="Thoth Workspace Registry"
|
||||
THT_WORKSPACE_GIT_AUTHOR_EMAIL=thoth-workspace-registry@example.invalid
|
||||
|
||||
# Set these for the selected DWH/vector/embedding adapters.
|
||||
THT_DB_NAME=
|
||||
THT_DWH_REST_URL=
|
||||
THT_VEC_REST_URL=
|
||||
THT_VEC_WRITE_REST_URL=
|
||||
THT_OLLAMA_URL=
|
||||
THT_DOCS_ROOT=/data/workspaces/example/evidence-source
|
||||
THT_PROFILE=server
|
||||
|
||||
# Local-vector defaults (used by the optional local-vector overlay).
|
||||
THT_VECTOR_DATABASE=thoth
|
||||
THT_VECTOR_BOOTSTRAP_USER=postgres
|
||||
THT_VECTOR_MIGRATOR_USER=thoth_vector_migrator
|
||||
THT_VECTOR_READER_USER=thoth_vector_reader
|
||||
THT_VECTOR_WRITER_USER=thoth_vector_writer
|
||||
THT_DB_NAME=warehouse
|
||||
THT_DWH_REST_URL=https://dwh.example.invalid
|
||||
THT_VEC_REST_URL=https://vector.example.invalid
|
||||
THT_VEC_WRITE_REST_URL=https://vector-write.example.invalid
|
||||
THT_OLLAMA_URL=https://embeddings.example.invalid
|
||||
|
||||
+33
-47
@@ -1,14 +1,3 @@
|
||||
# ThothII — deploy embedded nel portale omics_portal (PRODUZIONE).
|
||||
# core + frontend sulla rete esterna del portale (omics_portal_omics_network,
|
||||
# creata da Compose col prefisso project). Alias thothii-core/thothii-frontend
|
||||
# per il DNS usato dagli upstream nginx del portale.
|
||||
# NESSUNA porta host esposta: il backend è invisibile dall'esterno.
|
||||
#
|
||||
# Prereq: devono esistere entrambe le reti esterne: il portale crea
|
||||
# omics_portal_omics_network e lo stack vLLM crea localllm_default.
|
||||
# Avvia il portale con:
|
||||
# cd /home/chirone/omics_portal && docker compose up -d
|
||||
# Poi: docker compose up -d --build
|
||||
name: thothii
|
||||
|
||||
services:
|
||||
@@ -17,68 +6,65 @@ services:
|
||||
context: .
|
||||
dockerfile: docker/core.Dockerfile
|
||||
image: thothii-core:local
|
||||
env_file:
|
||||
- path: deploy/thothii.env
|
||||
required: false
|
||||
environment:
|
||||
HOST: 0.0.0.0
|
||||
PORT: "8787"
|
||||
THT_HARNESS_DIR: /app/harness
|
||||
THT_BIN: /opt/venv/bin/tht
|
||||
THT_DATA_ROOT: /data
|
||||
SETTINGS_FILE: /data/settings/settings.json
|
||||
THT_CONFIG: /app/harness/workspaces/local.yaml # configPath di default per i route tht senza workspace esplicito
|
||||
THT_MODEL_API_KEY_FILE: /data/secrets/model_api_key # provider key per buildPiChildEnv (codex)
|
||||
THT_WORKSPACE_REGISTRY_ROOT: /data/workspace-registry
|
||||
THT_WORKSPACE_GIT_REMOTE: ${THT_WORKSPACE_GIT_REMOTE:?set THT_WORKSPACE_GIT_REMOTE}
|
||||
THT_WORKSPACE_GIT_BRANCH: ${THT_WORKSPACE_GIT_BRANCH:-main}
|
||||
THT_WORKSPACE_INSTALLATION_ID: ${THT_WORKSPACE_INSTALLATION_ID:-server}
|
||||
THT_WORKSPACE_INSTALLATION_ID: ${THT_WORKSPACE_INSTALLATION_ID:-local}
|
||||
THT_WORKSPACE_GIT_AUTHOR_NAME: ${THT_WORKSPACE_GIT_AUTHOR_NAME:-Thoth Workspace Registry}
|
||||
THT_WORKSPACE_GIT_AUTHOR_EMAIL: ${THT_WORKSPACE_GIT_AUTHOR_EMAIL:-thoth-workspace-registry@localhost}
|
||||
THT_WORKSPACE_SECRET_ROOTS: /run/secrets
|
||||
GIT_CONFIG_COUNT: "2"
|
||||
GIT_CONFIG_KEY_0: credential.helper
|
||||
GIT_CONFIG_VALUE_0: store --file=/run/secrets/workspace-registry-git-credentials
|
||||
GIT_CONFIG_KEY_1: http.sslCAInfo
|
||||
GIT_CONFIG_VALUE_1: /run/secrets/workspace-registry-git-ca
|
||||
GIT_SSH_COMMAND: ssh -i /run/secrets/workspace-registry-git-ssh-key -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/run/secrets/workspace-registry-git-known-hosts
|
||||
AUTH_MODE: ${AUTH_MODE:-none}
|
||||
THT_DB_NAME: ${THT_DB_NAME:-}
|
||||
THT_DWH_REST_URL: ${THT_DWH_REST_URL:-}
|
||||
THT_VEC_REST_URL: ${THT_VEC_REST_URL:-}
|
||||
THT_VEC_WRITE_REST_URL: ${THT_VEC_WRITE_REST_URL:-}
|
||||
THT_OLLAMA_URL: ${THT_OLLAMA_URL:-}
|
||||
MAX_PI_PROCESSES: ${MAX_PI_PROCESSES:-4}
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway" # Supabase :5438 + Ollama :11434 sull'host
|
||||
volumes:
|
||||
- /home/chirone/thothii-data:/data
|
||||
- settings:/data/settings
|
||||
- pi-state:/home/thoth/.pi
|
||||
- workspace-registry:/data/workspace-registry
|
||||
- /home/chirone/thothii-data/pi-config:/home/thoth/.pi
|
||||
- ${PI_AUTH_FILE:?set PI_AUTH_FILE}:/home/thoth/.pi/agent/auth.json:ro
|
||||
- /home/chirone/chirone/etl/docs/evidence:/data/evidence:ro
|
||||
- ${THT_WORKSPACE_GIT_CREDENTIALS_FILE:-/dev/null}:/run/secrets/workspace-registry-git-credentials:ro
|
||||
- ${THT_WORKSPACE_GIT_CA_FILE:-/etc/ssl/certs/ca-certificates.crt}:/run/secrets/workspace-registry-git-ca:ro
|
||||
- ${THT_WORKSPACE_GIT_SSH_KEY_FILE:-/dev/null}:/run/secrets/workspace-registry-git-ssh-key:ro
|
||||
- ${THT_WORKSPACE_GIT_KNOWN_HOSTS_FILE:-/dev/null}:/run/secrets/workspace-registry-git-known-hosts:ro
|
||||
restart: unless-stopped
|
||||
- sessions:/data/sessions
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8787/health"]
|
||||
interval: 15s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
networks:
|
||||
omics_portal_omics_network:
|
||||
aliases: ["thothii-core"]
|
||||
localllm_default: {}
|
||||
- thothii
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/frontend.Dockerfile
|
||||
args:
|
||||
VITE_BASE: /datamart-builder/assets/
|
||||
VITE_BACKEND_URL: /datamart-builder/api
|
||||
VITE_BASE: /
|
||||
VITE_BACKEND_URL: /api
|
||||
image: thothii-frontend:local
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
core:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8080/ || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
networks:
|
||||
omics_portal_omics_network:
|
||||
aliases: ["thothii-frontend"]
|
||||
- thothii
|
||||
|
||||
networks:
|
||||
omics_portal_omics_network:
|
||||
external: true
|
||||
localllm_default:
|
||||
external: true
|
||||
thothii:
|
||||
|
||||
volumes:
|
||||
settings:
|
||||
pi-state:
|
||||
workspace-registry:
|
||||
sessions:
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
services:
|
||||
core:
|
||||
environment:
|
||||
# Non-secret settings come from the root .env interpolation file.
|
||||
AUTH_MODE: "${AUTH_MODE:-none}"
|
||||
THT_SECRETS_FILE: /run/secrets/thothii.secrets
|
||||
AUTH_MODE: none
|
||||
THT_WORKSPACE_INSTALLATION_ID: local
|
||||
ports:
|
||||
- "127.0.0.1:${THOTH_CORE_HTTP_PORT:-8787}:8787"
|
||||
restart: "no"
|
||||
|
||||
frontend:
|
||||
ports:
|
||||
- "127.0.0.1:${THOTH_HTTP_PORT:-8080}:8080"
|
||||
restart: "no"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
core:
|
||||
environment:
|
||||
AUTH_MODE: upstream
|
||||
THOTH_PUBLIC_EXPOSURE: "true"
|
||||
THT_DATA_ROOT: /data
|
||||
THT_WORKSPACE_INSTALLATION_ID: server
|
||||
volumes: !override
|
||||
- ${THT_DATA_ROOT:?set THT_DATA_ROOT}:/data
|
||||
- ${THT_PI_STATE_ROOT:?set THT_PI_STATE_ROOT}:/home/thoth/.pi
|
||||
- ${THT_WORKSPACE_REGISTRY_ROOT:?set THT_WORKSPACE_REGISTRY_ROOT}:/data/workspace-registry
|
||||
restart: unless-stopped
|
||||
|
||||
frontend:
|
||||
ports:
|
||||
- "${THOTH_SERVER_BIND:-127.0.0.1}:${THOTH_HTTP_PORT:-8080}:8080"
|
||||
restart: unless-stopped
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
# Local profile defaults. Copy this file to an untracked local.env and pass it with --env-file.
|
||||
# Values are non-secret documentation values only.
|
||||
THOTH_HTTP_PORT=8080
|
||||
THOTH_CORE_HTTP_PORT=8787
|
||||
MAX_PI_PROCESSES=4
|
||||
|
||||
THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git
|
||||
THT_WORKSPACE_GIT_BRANCH=main
|
||||
THT_WORKSPACE_GIT_AUTHOR_NAME="Thoth Workspace Registry"
|
||||
THT_WORKSPACE_GIT_AUTHOR_EMAIL=thoth-workspace-registry@example.invalid
|
||||
|
||||
THT_DB_NAME=warehouse
|
||||
THT_DWH_REST_URL=https://dwh.example.invalid
|
||||
THT_VEC_REST_URL=https://vector.example.invalid
|
||||
THT_VEC_WRITE_REST_URL=https://vector-write.example.invalid
|
||||
THT_OLLAMA_URL=https://embeddings.example.invalid
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
# Server profile defaults. Copy this file to a reviewed, untracked server.env and pass it with --env-file.
|
||||
# Values are non-secret documentation values only.
|
||||
THOTH_SERVER_BIND=127.0.0.1
|
||||
THOTH_HTTP_PORT=8080
|
||||
MAX_PI_PROCESSES=4
|
||||
|
||||
THT_DATA_ROOT=/srv/thothii/data
|
||||
THT_PI_STATE_ROOT=/srv/thothii/pi-state
|
||||
THT_WORKSPACE_REGISTRY_ROOT=/srv/thothii/workspace-registry
|
||||
THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git
|
||||
THT_WORKSPACE_GIT_BRANCH=main
|
||||
THT_WORKSPACE_GIT_AUTHOR_NAME="Thoth Workspace Registry"
|
||||
THT_WORKSPACE_GIT_AUTHOR_EMAIL=thoth-workspace-registry@example.invalid
|
||||
|
||||
THT_DB_NAME=warehouse
|
||||
THT_DWH_REST_URL=https://dwh.example.invalid
|
||||
THT_VEC_REST_URL=https://vector.example.invalid
|
||||
THT_VEC_WRITE_REST_URL=https://vector-write.example.invalid
|
||||
THT_OLLAMA_URL=https://embeddings.example.invalid
|
||||
@@ -1,43 +1,26 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
test -f .env.example
|
||||
test -f deploy/secrets/thothii.secrets.example
|
||||
grep -q '^docker compose up --build -d$' docs/installazione-docker-4-contesti.md
|
||||
if grep -q 'cp deploy/env.example deploy/.env\|THT_[A-Z0-9_]*_SECRET_FILE=' docs/installazione-docker-4-contesti.md; then
|
||||
echo "installation guide still presents the legacy per-file secret setup" >&2
|
||||
test -f deploy/env/local.env.example
|
||||
test -f deploy/env/server.env.example
|
||||
|
||||
rendered=$(mktemp)
|
||||
trap 'rm -f "$rendered"' EXIT HUP INT TERM
|
||||
|
||||
docker compose --env-file deploy/env/local.env.example \
|
||||
-f compose.yaml -f deploy/compose.local.yaml config >"$rendered"
|
||||
|
||||
grep -q '^ core:' "$rendered"
|
||||
grep -q '^ frontend:' "$rendered"
|
||||
grep -q 'host_ip: 127.0.0.1' "$rendered"
|
||||
grep -q 'AUTH_MODE: none' "$rendered"
|
||||
grep -q 'THT_WORKSPACE_INSTALLATION_ID: local' "$rendered"
|
||||
if grep -Eqi 'omics_portal|chirone|localllm_default|/home/chirone' "$rendered"; then
|
||||
echo "default Compose contains application-specific coupling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$tmp/deploy/secrets" "$tmp/deploy/workspaces"
|
||||
cp compose.yaml "$tmp/compose.yaml"
|
||||
cp .env.example "$tmp/.env"
|
||||
cp deploy/secrets/thothii.secrets.example "$tmp/deploy/secrets/thothii.secrets"
|
||||
printf '%s\n' 'THT_MODEL_API_KEY=example-secret' >>"$tmp/deploy/secrets/thothii.secrets"
|
||||
chmod 0600 "$tmp/deploy/secrets/thothii.secrets"
|
||||
|
||||
services=$(docker compose --project-directory "$tmp" config --services)
|
||||
[ "$services" = "core
|
||||
frontend" ] || {
|
||||
echo "default Compose services must be core and frontend (got: $services)" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
rendered=$(docker compose --project-directory "$tmp" config)
|
||||
printf '%s\n' "$rendered" | grep -q 'target: thothii.secrets'
|
||||
if printf '%s\n' "$rendered" | grep -Eq 'dwh_api_key|vector_reader_api_key|vector_writer_api_key|model_api_key|thoth_ca'; then
|
||||
echo "default Compose must not declare legacy per-secret mounts" >&2
|
||||
exit 1
|
||||
fi
|
||||
if printf '%s\n' "$rendered" | grep -Eq 'THT_[A-Z0-9_]+_SECRET_FILE:'; then
|
||||
echo "default Compose must not require legacy secret-file variables" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' "$rendered" | grep -q 'THT_SECRETS_FILE: /run/secrets/thothii.secrets'
|
||||
|
||||
echo "default Compose contract passed."
|
||||
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT HUP INT TERM
|
||||
|
||||
render_profile() {
|
||||
local profile=$1
|
||||
local env_file=$2
|
||||
local compose_file=$3
|
||||
local rendered="$tmp/$profile.json"
|
||||
|
||||
docker compose --env-file "$env_file" -f compose.yaml -f "$compose_file" \
|
||||
config --format json >"$rendered"
|
||||
|
||||
node - "$rendered" "$profile" <<'NODE'
|
||||
const fs = require("fs");
|
||||
|
||||
const [configPath, profile] = process.argv.slice(2);
|
||||
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
||||
const services = Object.keys(config.services).sort();
|
||||
if (services.join(",") !== "core,frontend") throw new Error("mandatory stack must be core,frontend");
|
||||
if (/omics_portal|chirone|localllm_default|\/home\/chirone/i.test(JSON.stringify(config))) {
|
||||
throw new Error("forbidden application coupling");
|
||||
}
|
||||
if (!config.networks || !config.networks.thothii) throw new Error("base stack must define the thothii network");
|
||||
|
||||
const ports = Object.fromEntries(
|
||||
Object.entries(config.services).map(([name, service]) => [name, service.ports || []]),
|
||||
);
|
||||
if (profile === "local") {
|
||||
if (!ports.frontend.some((port) => port.host_ip === "127.0.0.1")) {
|
||||
throw new Error("local frontend must publish a loopback port");
|
||||
}
|
||||
if (ports.core.length !== 0 && !ports.core.every((port) => port.host_ip === "127.0.0.1")) {
|
||||
throw new Error("local core may publish only loopback ports");
|
||||
}
|
||||
} else {
|
||||
if (ports.core.length !== 0) throw new Error("server core must not publish a host port");
|
||||
if (ports.frontend.length === 0) throw new Error("server frontend must publish a host port");
|
||||
}
|
||||
NODE
|
||||
}
|
||||
|
||||
assert_remote_required() {
|
||||
local env_file=$1
|
||||
local compose_file=$2
|
||||
local without_remote="$tmp/without-remote.env"
|
||||
grep -v '^THT_WORKSPACE_GIT_REMOTE=' "$env_file" >"$without_remote"
|
||||
|
||||
if env -u THT_WORKSPACE_GIT_REMOTE docker compose --env-file "$without_remote" -f compose.yaml -f "$compose_file" \
|
||||
config --format json >"$tmp/missing-remote.out" 2>"$tmp/missing-remote.err"; then
|
||||
echo "Compose must require THT_WORKSPACE_GIT_REMOTE" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q 'THT_WORKSPACE_GIT_REMOTE' "$tmp/missing-remote.err"
|
||||
}
|
||||
|
||||
THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git \
|
||||
PI_AUTH_FILE=/dev/null \
|
||||
docker compose -f compose.yaml config --format json >"$tmp/base.json"
|
||||
node - "$tmp/base.json" <<'NODE'
|
||||
const fs = require("fs");
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
||||
const services = Object.keys(config.services).sort();
|
||||
if (services.join(",") !== "core,frontend") throw new Error("mandatory stack must be core,frontend");
|
||||
if (/omics_portal|chirone|localllm_default|\/home\/chirone/i.test(JSON.stringify(config))) {
|
||||
throw new Error("forbidden application coupling");
|
||||
}
|
||||
if (!config.networks || !config.networks.thothii) throw new Error("base stack must define the thothii network");
|
||||
for (const volume of ["settings", "pi-state", "workspace-registry", "sessions"]) {
|
||||
if (!config.volumes || !config.volumes[volume]) throw new Error(`missing required volume: ${volume}`);
|
||||
}
|
||||
NODE
|
||||
|
||||
render_profile local deploy/env/local.env.example deploy/compose.local.yaml
|
||||
render_profile server deploy/env/server.env.example deploy/compose.server.yaml
|
||||
assert_remote_required deploy/env/local.env.example deploy/compose.local.yaml
|
||||
assert_remote_required deploy/env/server.env.example deploy/compose.server.yaml
|
||||
|
||||
echo "unified Compose contract passed."
|
||||
Reference in New Issue
Block a user