feat(compose): cluster-by-default Caddy-fronted stack with bare-metal join

`docker compose up` from a clone builds one image and brings up the whole stack
— PostgreSQL, console, Caddy, channel, and 10 server nodes — sharing one
Postgres so the console discovers every node. The dashboard is reachable only
through Caddy (HTTP/2 avoids the browser's 6-connection cap on the dashboard's
SSE streams); the console's plain-HTTP port is no longer published. Postgres
binds 127.0.0.1 so a bare-metal turnstone-server can join the cluster — the
bare-metal overlay is folded in and removed. Insecure dev defaults keep it
zero-config; the bundled production stack mirrors the shape but pulls ghcr
images and requires real secrets.

Move the Caddyfile under turnstone/deploy so it ships in the wheel; update docs,
QUICKSTART, and .env.example to match.
This commit is contained in:
Patrick Buckley
2026-05-31 16:00:34 -07:00
parent c58df26a30
commit 631f1b0021
11 changed files with 524 additions and 424 deletions
+45 -33
View File
@@ -1,49 +1,61 @@
# =============================================================================
# Turnstone Environment Variables
# Copy to .env and adjust values for your deployment.
# Turnstone environment overrides — ALL OPTIONAL for the dev stack.
#
# Usage:
# Single node: docker compose --profile production up
# 10-node cluster: docker compose --profile cluster up
# `docker compose up` from a clone works with zero config: every value below
# has a built-in (insecure) default. Copy this file to `.env` only to override.
#
# The PRODUCTION stack (turnstone/deploy/compose.yaml) has no baked-in secrets
# and DOES require TURNSTONE_JWT_SECRET and POSTGRES_PASSWORD.
#
# Note: for a turnstone process running on bare metal (not in a container),
# put secrets in ~/.config/turnstone/config.toml (chmod 0600), not the
# environment. See docs/docker.md "Join a bare-metal host".
# =============================================================================
# -- LLM Backend --------------------------------------------------------------
LLM_BASE_URL=http://host.docker.internal:8000/v1
OPENAI_API_KEY=dummy
# ANTHROPIC_API_KEY=sk-ant-...# Set instead of OPENAI_API_KEY for Anthropic
# TAVILY_API_KEY=tvly-... # Web search fallback (local models only)
# MODEL=# Override default model alias
# -- LLM backend --------------------------------------------------------------
# Optional: nodes boot without an LLM. Add real model backends from the console
# UI (Models tab). These only set the bootstrap default a node starts with.
# LLM_BASE_URL=http://host.docker.internal:8000/v1
# OPENAI_API_KEY=dummy
# ANTHROPIC_API_KEY=sk-ant-... # set instead of OPENAI_API_KEY for Anthropic
# TAVILY_API_KEY=tvly-... # web-search fallback (local models only)
# MODEL= # default model alias
# -- Authentication (required) ------------------------------------------------
# Generate with: python -c "import secrets; print(secrets.token_hex(32))"
TURNSTONE_JWT_SECRET=changeme-to-32-bytes-of-hex
# -- Secrets ------------------------------------------------------------------
# The dev stack defaults these to INSECURE values. Always set real ones for
# anything reachable beyond localhost. Generate the JWT secret with:
# python -c "import secrets; print(secrets.token_hex(32))"
# TURNSTONE_JWT_SECRET=
# POSTGRES_PASSWORD=
# -- Database ------------------------------------------------------------------
# Single-node default is SQLite (zero config). Set these for PostgreSQL:
# -- Database -----------------------------------------------------------------
# Defaults to the bundled PostgreSQL (shared by every service — required for
# the console to discover nodes). Override to point at an external database:
# TURNSTONE_DB_BACKEND=postgresql
# POSTGRES_USER=turnstone
# POSTGRES_PASSWORD=changeme
# TURNSTONE_DB_URL=postgresql+psycopg://turnstone:changeme@postgres:5432/turnstone
# TURNSTONE_DB_URL=postgresql+psycopg://turnstone:<pw>@postgres:5432/turnstone
# -- Ports ---------------------------------------------------------------------
# SERVER_PORT=8080
# CONSOLE_PORT=8090
# -- Ports / networking -------------------------------------------------------
# The dashboard is reached via Caddy only (HTTP/2 avoids the browser's
# 6-connection cap on the console's SSE streams). Both stacks expose the same
# two host ports; everything else is proxied through the console.
# CONSOLE_HTTPS_PORT=8443 # Caddy (dashboard HTTPS)
# POSTGRES_PORT=5432 # exposed for bare-metal host joins
# POSTGRES_BIND=127.0.0.1 # set 0.0.0.0 to let another machine join
# -- Workspace -----------------------------------------------------------------
# Bind-mount a host directory into the container at /workspace.
# The model can read/write files here. Default: empty Docker volume.
# -- Workspace ----------------------------------------------------------------
# Bind-mount a host directory the model can read/write at /workspace:
# WORKSPACE_MOUNT=/path/to/your/project
# -- Agent behavior ------------------------------------------------------------
# SKIP_PERMISSIONS=true # Auto-approve all tool calls (dev only)
# MCP_CONFIG=/workspace/mcp.json# MCP server configuration file
# -- Agent behavior -----------------------------------------------------------
# SKIP_PERMISSIONS=true # auto-approve all tool calls (dev only)
# MCP_CONFIG=/workspace/mcp.json # MCP server config file
# -- Discord channel gateway ---------------------------------------------------
# -- Channel gateway (Discord / Slack) ----------------------------------------
# TURNSTONE_DISCORD_TOKEN=
# TURNSTONE_DISCORD_GUILD=0
# TURNSTONE_SLACK_TOKEN=xoxb-...
# TURNSTONE_SLACK_APP_TOKEN=xapp-...
# -- Cluster (profile: cluster) -----------------------------------------------
# These are set per-node in compose.yaml; only override for custom topologies.
# TURNSTONE_NODE_ID=node-1
# TURNSTONE_ADVERTISE_URL=http://server-1:8080
# -- Production image tag ------------------------------------------------------
# TURNSTONE_IMAGE_TAG=latest # pin the ghcr.io image (production stack)