-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.multitenant.yml
More file actions
160 lines (154 loc) · 6.53 KB
/
Copy pathdocker-compose.multitenant.yml
File metadata and controls
160 lines (154 loc) · 6.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# Shared ACS app definition for blue/green slots. Only one slot receives traffic
# (caddy/acs_upstream.caddyfile). Deploy brings up the idle slot, waits healthy,
# reloads Caddy upstream, then stops the previous slot.
x-steward-acs: &steward_acs
build:
context: .
dockerfile: Dockerfile
target: release
args:
REPO_ADAPTER: postgres
image: naharemete/steward_acs:${ACS_IMAGE_TAG:-multitenant}
restart: unless-stopped
stop_grace_period: 25s
expose:
- "4001"
environment:
MIX_ENV: prod
PORT: "4001"
PHX_HOST: "${PHX_HOST:-prod.stewardacs.xyz}"
MULTI_TENANT: "true"
ACS_ORG_NAME: "${ACS_ORG_NAME:-default}"
BASE_DOMAIN: "${BASE_DOMAIN:-stewardacs.xyz}"
REPO_ADAPTER: postgres
# Neon/Postgres. Empty DATABASE_PATH so runtime does not force SQLite.
DATABASE_PATH: ""
DATABASE_URL: "${DATABASE_URL:?Set DATABASE_URL to Neon postgres connection string}"
PGSSL: "${PGSSL:-true}"
POOL_SIZE: "${POOL_SIZE:-10}"
ORGS_FILE: /data/orgs.yaml
SECRET_KEY_BASE: "${SECRET_KEY_BASE:?Set SECRET_KEY_BASE}"
MCP_API_KEY: "${MCP_API_KEY:?Set MCP_API_KEY}"
MCP_QUERY_KEY_AUTH: "${MCP_QUERY_KEY_AUTH:-false}"
# Multi-tenant prod serves Claude/ChatGPT connectors; default on so a missing
# thin-env flag does not silently kill OAuth (API-key-only 401s, 404 PRM).
OAUTH_BEARER_ENABLED: "${OAUTH_BEARER_ENABLED:-true}"
AUTH0_DOMAIN: "${AUTH0_DOMAIN:-}"
AUTH0_AUDIENCE: "${AUTH0_AUDIENCE:-}"
AUTH0_ISSUER: "${AUTH0_ISSUER:-}"
AUTH0_CONNECTION: "${AUTH0_CONNECTION:-}"
# Management API — provisioner ensures per-org MCP audiences on create.
AUTH0_MGMT_CLIENT_ID: "${AUTH0_MGMT_CLIENT_ID:-}"
AUTH0_MGMT_CLIENT_SECRET: "${AUTH0_MGMT_CLIENT_SECRET:-}"
OAUTH_FIXED_DCR_CLIENT_ID: "${OAUTH_FIXED_DCR_CLIENT_ID:-}"
MCP_PUBLIC_URL: "${MCP_PUBLIC_URL:-https://prod.stewardacs.xyz}"
MCP_RESOURCE_URL: "${MCP_RESOURCE_URL:-https://prod.stewardacs.xyz/mcp/sse}"
ACCOUNT_HOST: "${ACCOUNT_HOST:-prod.stewardacs.xyz}"
OIDC_BROWSER_ENABLED: "${OIDC_BROWSER_ENABLED:-true}"
AUTH0_WEB_CLIENT_ID: "${AUTH0_WEB_CLIENT_ID:-}"
AUTH0_WEB_CLIENT_SECRET: "${AUTH0_WEB_CLIENT_SECRET:-}"
AUTH0_WEB_REDIRECT_URI: "${AUTH0_WEB_REDIRECT_URI:-https://prod.stewardacs.xyz/auth/callback}"
SELF_SERVICE_ORGS_ENABLED: "${SELF_SERVICE_ORGS_ENABLED:-true}"
OLLAMA_URL: http://ollama:11434
MEMORY_STORE: database
# LLM providers (Infisical). Compose --env-file alone does not inject into
# the container — each key must be listed here for memory/skill auditing.
ENABLED_LLM_PROVIDERS: "${ENABLED_LLM_PROVIDERS:-}"
NIM_API_KEY: "${NIM_API_KEY:-}"
MIMO_API_KEY: "${MIMO_API_KEY:-}"
MINIMAX_API_KEY: "${MINIMAX_API_KEY:-}"
OPENAI_API_KEY: "${OPENAI_API_KEY:-}"
OPENAI_BASE_URL: "${OPENAI_BASE_URL:-}"
OPENAI_MODEL: "${OPENAI_MODEL:-}"
OPENROUTER_API_KEY: "${OPENROUTER_API_KEY:-}"
OPENROUTER_BASE_URL: "${OPENROUTER_BASE_URL:-}"
OPENROUTER_MODEL: "${OPENROUTER_MODEL:-}"
# Per-location, per-call-type LLM routing (see Acs.LLM.Router).
ACS_REGION: "${ACS_REGION:-default}"
LLM_PRIORITY_INTAKE: "${LLM_PRIORITY_INTAKE:-}"
LLM_PRIORITY_SKILL_INTAKE: "${LLM_PRIORITY_SKILL_INTAKE:-}"
LLM_MODEL_INTAKE: "${LLM_MODEL_INTAKE:-}"
# Production observability (disabled when AXIOM_LOGS is empty).
AXIOM_LOGS: "${AXIOM_LOGS:-}"
AXIOM_DATASET: "${AXIOM_DATASET:-steward_logs}"
AXIOM_AGENT_OPS_DATASET: "${AXIOM_AGENT_OPS_DATASET:-steward_meta_analytics}"
# Edge deployment URL — api.axiom.co does not route /v1/ingest (events 404 silently).
AXIOM_DOMAIN: "${AXIOM_DOMAIN:-https://us-east-1.aws.edge.axiom.co}"
# Hourly Meta-Harness: ops table + rollups shipped to AXIOM_AGENT_OPS_DATASET.
META_HARNESS_ENABLED: "${META_HARNESS_ENABLED:-true}"
# Invitation email (optional — copy-link only when either is empty).
RESEND_API_KEY: "${RESEND_API_KEY:-}"
RESEND_FROM_EMAIL: "${RESEND_FROM_EMAIL:-}"
volumes:
- acs_data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4001/mcp/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 20s
services:
caddy:
image: caddy:2-alpine
container_name: steward_caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile.multitenant:/etc/caddy/Caddyfile:ro
- ./caddy/acs_upstream.caddyfile:/etc/caddy/acs_upstream.caddyfile:ro
- ./certs:/etc/caddy/certs:ro
- caddy_data:/data
environment:
BASE_DOMAIN: "${BASE_DOMAIN:-stewardacs.xyz}"
AUTH0_DOMAIN: "${AUTH0_DOMAIN:-dev-jw5wgp2b.us.auth0.com}"
AUTH0_AUDIENCE_URLENC: "${AUTH0_AUDIENCE_URLENC:-https%3A%2F%2Fprod.stewardacs.xyz%2Fmcp%2Fsse}"
# No depends_on ACS: blue/green cutover keeps Caddy up while the idle slot boots.
steward_acs_blue:
<<: *steward_acs
container_name: steward_acs_blue
steward_acs_green:
<<: *steward_acs
container_name: steward_acs_green
# Host CPU/memory/disk/network → Axiom Metrics (opt-in via COMPOSE_PROFILES=axiom).
# Needs AXIOM_LOGS (Infisical) + Metrics dataset AXIOM_METRICS_DATASET.
otel_collector:
profiles: ["axiom"]
image: otel/opentelemetry-collector-contrib:0.129.1
container_name: steward_otel
restart: unless-stopped
command: ["--config=/etc/otelcol-contrib/config.yaml"]
environment:
AXIOM_LOGS: "${AXIOM_LOGS:?Set AXIOM_LOGS to enable host metrics}"
AXIOM_METRICS_DATASET: "${AXIOM_METRICS_DATASET:-steward-acs-metrics}"
# Edge deployment URL preferred for OTLP (see Axiom edge docs).
AXIOM_DOMAIN: "${AXIOM_DOMAIN:-https://us-east-1.aws.edge.axiom.co}"
volumes:
- ./otel/collector-config.yaml:/etc/otelcol-contrib/config.yaml:ro
- /:/hostfs:ro,rslave
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
expose:
- "11434"
volumes:
- ollama_data:/root/.ollama
environment:
# Keep nomic-embed-text resident so embedding calls never pay a cold-start
# reload (default unloads after 5 min idle -> multi-second spikes on a 2-CPU box).
OLLAMA_KEEP_ALIVE: "-1"
# Health = serve is up AND the embedding model is present. Without the
# model grep this stays "healthy" while every embed call 404s. If the model
# is missing, scripts/ensure-ollama-model.sh (cron + @reboot) pulls it.
healthcheck:
test: ["CMD-SHELL", "ollama list | grep -q nomic-embed-text"]
interval: 30s
timeout: 10s
retries: 3
start_period: 120s
volumes:
acs_data:
caddy_data:
ollama_data: