ThothII

ThothII is a human-reviewed NL-to-SQL workflow with a React frontend and a Fastify/Pi/tht core. The portable deployment runs exactly two application services; data services remain external in this profile.

Docker Compose: one-command startup

Requirements: Docker Engine with Compose v2. The default project starts only the two application images; DWH, vector and embedding services can be remote or supplied by an optional overlay.

From a fresh clone, run these commands from the repository root:

cp .env.example .env
cp deploy/secrets/thothii.secrets.example deploy/secrets/thothii.secrets
chmod 600 deploy/secrets/thothii.secrets
# Edit .env (non-secret endpoints) and deploy/secrets/thothii.secrets (KEY=VALUE lines).
docker compose up --build -d

The root .env is loaded automatically by Compose. It defaults to compose.yaml, an empty profile, and THT_SECRETS_FILE=deploy/secrets/thothii.secrets; no --env-file, -f, or --profile flag is required for the normal installation. Add or edit YAML workspace descriptors under deploy/workspaces/; they are mounted read-only and relative roots resolve beneath /data/workspaces/<workspace-name>. Open http://127.0.0.1:8080 (set THOTH_HTTP_PORT in .env to choose another loopback port).

The bundle contains only values, one per line (THT_MODEL_API_KEY=..., DWH/vector keys, and the optional local-vector passwords). It is ignored by Git and never copied into either image. Do not put credentials in .env, workspace YAML, URLs, or Compose interpolation values.

Optional overlays

Overlays are selected in .env, so the operational command remains the same. On Unix-like systems use : between files; on Windows use ;:

# Remote DWH/vector/embedding services with authenticated reverse proxy:
COMPOSE_FILE=compose.yaml:deploy/compose.production.yaml
COMPOSE_PROFILES=

# Local pgvector (Mac/Windows or a standalone application server):
COMPOSE_FILE=compose.yaml:deploy/compose.local-vector.yaml
COMPOSE_PROFILES=local-vector

After changing .env, apply the selected configuration with docker compose up --build -d. Preprocessing is an explicit opt-in preset: append deploy/compose.preprocess.yaml:deploy/compose.preprocess-local-vector.yaml and set COMPOSE_PROFILES=local-vector,preprocess; then run the job with docker compose run --rm preprocess-evidence or preprocess-dwh.

Application state, including settings, sessions, artifacts, and indexes, lives in the named thoth_data volume mounted at /data. docker compose down keeps that volume. Only an explicit destructive command such as docker compose down --volumes removes it.

The frontend depends on the core health check and proxies /health and /api/* to it. The application health endpoint intentionally checks process readiness only; external dependency diagnostics are exposed by tht doctor and do not prevent the UI from starting.

Run the end-to-end packaging check with:

./scripts/docker-smoke.sh

The smoke script validates Compose, builds and waits for both services, checks health through the frontend, verifies SSE response headers, restarts the core, and confirms /data survives. Each run uses a unique Compose project and removes that project's containers, network, and test volume afterward. It never targets the fixed thothii operator project or its volume. Set SMOKE_PROJECT to a different explicit project name for reproducible debugging, and set KEEP_SMOKE_RESOURCES=1 to retain that smoke project's resources for inspection; remove them later with docker compose --project-name "$SMOKE_PROJECT" down --volumes.

Optional local pgvector and recovery

The local-vector overlay reads THT_VECTOR_BOOTSTRAP_PASSWORD, THT_VECTOR_MIGRATOR_PASSWORD, THT_VECTOR_READER_PASSWORD, and THT_VECTOR_WRITER_PASSWORD from the same bundle. Its vector_data volume is independent of application state; passwords are selected at runtime and are never passed as URL arguments.

Preprocessing jobs and S3 Evidence

The included job workspaces target the local-vector profile. Put the four local-vector password keys in the bundle, set THT_OLLAMA_URL, mount Evidence at /data/source/evidence, then select the preprocessing preset in .env:

COMPOSE_FILE=compose.yaml:deploy/compose.local-vector.yaml:deploy/compose.preprocess.yaml:deploy/compose.preprocess-local-vector.yaml
COMPOSE_PROFILES=local-vector,preprocess

Run docker compose run --rm preprocess-evidence or docker compose run --rm preprocess-dwh. The overlay makes each job wait for the vector database health check, role reconciliation, and a successful migration; no separate database startup or migration command is required.

S3 Evidence uses the optional tht[s3] dependency and canonical s3://bucket/key provenance. AWS endpoints are used when no custom URL is supplied. Every custom endpoint is an explicit egress trust-boundary opt-in and uses path-style addressing; private and HTTP endpoints require additional independent opt-ins. Literal non-global IPv4/IPv6 addresses are classified locally; hostnames are not DNS-pinned, so trusted custom-endpoint deployments must enforce their destination with network egress policy. Store access key, secret key, and session token as secret references in deployment configuration—never in Compose environment values or source URIs. Discovery and reads are bounded by configured page, object, and byte limits.

Create a versioned PostgreSQL custom-format backup (the filename is operator-controlled, so use an immutable timestamp or release identifier):

./scripts/vector-backup.sh \
  --host 127.0.0.1 --port 5432 --database thoth --user thoth_backup \
  --password-file /secure/thoth/vector-backup-password \
  --output /secure/backups/thoth-vectors-2026-07-12.dump

The dump contains the three allowlisted vectors tables, their data and ACLs, plus the public.tht_vector_migrations ledger. Login roles and passwords are deliberately not copied: provision/reconcile the approved role names on the target first, and install the vector extension in its vectors schema. The target must otherwise contain no vector tables or ledger.

Restore always names both the currently active source and a target on a physically distinct PostgreSQL cluster. The script compares PostgreSQL system identity, so host aliases or a different database in the active cluster cannot bypass the guard. It refuses a non-empty target unless --force-nonempty is explicit, and the clean restore is one transaction:

./scripts/vector-restore.sh \
  --active-host vector-db --active-database thoth --active-user thoth_backup \
  --active-password-file /secure/thoth/vector-active-password \
  --target-host vector-db-restore --target-database thoth --target-user thoth_restore \
  --target-password-file /secure/thoth/vector-restore-password \
  --input /secure/backups/thoth-vectors-2026-07-12.dump

After restore, run tht vector migrate --status --json, adapter health, and a known retrieval query against the target before changing any deployment endpoint. Never test recovery against the active vector_data volume. ./scripts/local-vector-smoke.sh --backup-restore performs this drill with disposable source and target volumes.

Production trust boundary and secrets

ThothII does not implement OIDC. Do not expose its application port directly to a network. The production pattern is an authenticated host reverse proxy that:

  • terminates TLS and authenticates every request;
  • removes any client-supplied identity header;
  • injects one trusted X-Authenticated-User value;
  • proxies to the loopback-only ThothII frontend.

deploy/nginx-authenticated-proxy.conf.example shows the contract using nginx auth_request; replace the placeholder authentication gateway with the organization's reviewed identity proxy. AUTH_MODE=upstream trusts this boundary and rejects requests without the identity header. Setting THOTH_PUBLIC_EXPOSURE=true with any other auth mode fails during core startup.

Production credentials use the one Compose secret bundle, not .env. Put the required keys in deploy/secrets/thothii.secrets and select the production overlay in .env:

THT_MODEL_API_KEY=replace-me
THT_DWH_API_KEY=replace-me
THT_VEC_API_KEY=replace-me
THT_VEC_WRITE_API_KEY=replace-me

The bundle is mounted read-only as /run/secrets/thothii.secrets and must be mode 0600 or 0400 on the host. Docker's runtime 0444 mode is accepted only beneath /run/secrets; see deploy/secrets/README.md. A PEM CA chain is deliberately not a bundle value: PEM contains whitespace and is rejected by the strict parser. Keep the CA chain in the host/secret-manager materialization and add a reviewed Compose override that mounts it at /run/secrets/ca-chain.pem and sets THT_SSL_CA when a private CA is required. The base bundle does not create that mount. The frontend remains on loopback; the authenticated host proxy is the only public listener.

Set the selected model provider in application settings (or PI_PROVIDER). For each Pi spawn the backend validates and reads THT_MODEL_API_KEY from the bundle, then exposes its value only as the provider's recognized child variable (for example ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, or ZAI_API_KEY). Neither the generic file path nor deprecated PI_PROVIDER_API_KEY is inherited by Pi. Local providers such as Ollama require no model key.

THT_MODEL_API_KEY supports Pi providers whose authentication is exactly one key: ant-ling, anthropic, cerebras, deepseek, fireworks, github-copilot, google (including the gemini alias), google-vertex when using its API-key mode, groq, huggingface, kimi-coding, minimax, minimax-cn, mistral, moonshotai, moonshotai-cn, nvidia, openai, opencode, opencode-go, openrouter, together, vercel-ai-gateway, xai, the four xiaomi* providers, zai, and zai-coding-cn. Compound providers are deliberately unsupported: amazon-bedrock, azure-openai-responses, cloudflare-workers-ai, and cloudflare-ai-gateway require multiple credential/configuration values. Selecting one fails before Pi starts; ambient AWS, Azure, and Cloudflare credentials are still scrubbed. Supporting them requires a future dedicated provider-specific configuration.

Reproducible image verification

Base images use exact tags and immutable multi-platform manifest digests. Dependency update and residual OS-repository limitations are documented in docker/LOCKS.md. Run the shared architecture gate with PLATFORM=linux/amd64 or PLATFORM=linux/arm64:

PLATFORM=linux/arm64 ./scripts/verify-container-images.sh

It builds both images, runs common version/runtime/security smokes, and emits an image/package inventory beneath .artifacts/container-images/. CI runs the same script for both architectures.

S
Description
ThothII
Readme
35 MiB
Languages
TypeScript 46.1%
Python 18%
Go 15.1%
JavaScript 12.6%
Shell 4.9%
Other 3.3%