-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
304 lines (295 loc) · 12.7 KB
/
Copy pathdocker-compose.yml
File metadata and controls
304 lines (295 loc) · 12.7 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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# =============================================================================
# ChatBotKit Platform - full local stack in one command
# =============================================================================
# Three ways to use it - no mode ever builds or starts another mode's
# services:
#
# docker compose up # THE DEFAULT: a ready dev
# # server. The checkout is
# # copied into a container,
# # installed, provisioned
# # (SQLite), and `next dev`
# # runs against redis - hot
# # reload, no image build
# docker compose --profile distro up --build platform
# # the full compiled stack.
# # WARNING: this compiles the
# # entire platform - expect
# # 10-15+ minutes on first
# # build depending on hardware
# docker compose --profile distro up --no-build --pull always platform
# # pull the prebuilt community
# # app and initializer images
# docker compose up redis # backing services only,
# # then `pnpm dev` on the host
#
# The dev server mounts the checkout READ-ONLY and copies it into the
# container, installing there - the host tree is never written, so it runs
# identically from a plain checkout and when nested in another workspace. The
# .devcontainer provides the host-side toolchain and works with all three.
#
# The end-user distribution - the image-only stack published as a Compose OCI
# artifact (`docker compose -f oci://ghcr.io/chatbotkit/platform-community:latest
# up -d`) - lives separately in docker/distro/, one folder per package flavor.
# =============================================================================
# @note the storage environment every application service shares. The store
# is Garage, spoken to over the plain S3 protocol - see docker/garage/.
# The credentials are the known development values garage-init provisions.
#
# @note the store is a separate, published service with one name everywhere,
# like the relay and the app shells: browsers resolve `cbk-storage.localhost`
# to loopback with no DNS setup, and inside the network the garage service
# carries it as an alias. Presigned URLs embed this endpoint and are fetched
# by both browsers and the application itself, so the two must agree. (When
# running `pnpm dev` on the host instead, STORAGE_ENDPOINT=http://localhost:3900
# in platform/.env does the same job.)
x-storage-env: &storage-env
STORAGE_ENDPOINT: http://cbk-storage.localhost:3900
STORAGE_REGION: garage
STORAGE_ACCESS_KEY_ID: GK31e57eba9df26b2e7e1b0eaa
STORAGE_SECRET_ACCESS_KEY: 9f3c1e2b8a4d5f6071829304a5b6c7d8e9f00112233445566778899aabbccdde
STORAGE_FORCE_PATH_STYLE: 'true'
FILE_S3_BUCKET_NAME: file
IMAGE_S3_BUCKET_NAME: image
VIDEO_S3_BUCKET_NAME: video
AUDIO_S3_BUCKET_NAME: audio
CONVERSATION_S3_BUCKET_NAME: conversation
NAMESPACE_S3_BUCKET_NAME: namespace
SESSION_S3_BUCKET_NAME: session
SPACE_S3_BUCKET_NAME: space
TEMP_S3_BUCKET_NAME: temp
OUTPUT_S3_BUCKET_NAME: output
services:
platform:
profiles: ['distro']
image: ${PLATFORM_IMAGE:-ghcr.io/chatbotkit/platform-community-app:next}
build:
context: .
dockerfile: docker/Dockerfile
args:
# @note override for a real deployment: the site url is baked into the
# build by Next.js
SITE_URL: ${SITE_URL:-http://cbk.localhost:3000}
SPACE_APEX: ${SPACE_APEX:-cbk-space.localhost}
PORTAL_APEX: ${PORTAL_APEX:-cbk-portal.localhost}
APP_MAIN_ORIGIN: ${APP_MAIN_ORIGIN:-http://cbk-apps.localhost:3000}
APP_LABS_ORIGIN: ${APP_LABS_ORIGIN:-http://cbk-labs.localhost:3000}
ports:
- '3000:3000'
# @note the built-in realtime relay - see RELAY_URL below
- '${RELAY_PORT:-3001}:3001'
environment:
<<: *storage-env
NODE_ENV: production
PORT: 3000
SITE_URL: ${SITE_URL:-http://cbk.localhost:3000}
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://cbk.localhost:3000}
# @note realtime channels meet at a relay the platform process hosts on
# RELAY_PORT - see docker/distro/community/compose.yml
RELAY_PORT: 3001
RELAY_URL: ${RELAY_URL:-http://cbk-relay.localhost:3001}
SPACE_APEX: ${SPACE_APEX:-cbk-space.localhost}
PORTAL_APEX: ${PORTAL_APEX:-cbk-portal.localhost}
# @note the app shells, baked the same way: the main shell at
# `cbk-apps.localhost:3000`, the labs shell at
# `cbk-labs.localhost:3000`. Cookies do not cross hosts, so sign in on
# the shell host itself
APP_MAIN_ORIGIN: ${APP_MAIN_ORIGIN:-http://cbk-apps.localhost:3000}
APP_LABS_ORIGIN: ${APP_LABS_ORIGIN:-http://cbk-labs.localhost:3000}
# @note left empty, the image generates these secrets on first boot and
# persists them in the platform-data volume - see docker/entrypoint.sh;
# set explicitly to override
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-}
QUEUE_SECRET: ${QUEUE_SECRET:-}
JWT_TOKEN_SECRET_KEY: ${JWT_TOKEN_SECRET_KEY:-}
CLOAK_ENCRYPTION_KEY: ${CLOAK_ENCRYPTION_KEY:-}
PRISMA_DATABASE_URL: file:/data/chatbotkit.db
# @note sandbox workspaces - what agents write and install - live in the
# data volume so they survive restarts; see packages/sandbox/README.md
SANDBOX_DATA_DIR: /data/sandbox
# @note optional: encrypts stored credentials in the database; unset,
# they are stored as given. See docs/configuration.md, "Encryption at rest"
PRISMA_FIELD_ENCRYPTION_KEY: ${PRISMA_FIELD_ENCRYPTION_KEY:-}
REDIS_URL: redis://redis:6379
QDRANT_URL: http://qdrant:6333
# @note extra configuration (provider keys and the like) goes here; the
# file is optional. Values can also be persisted in the platform-data
# volume with `docker compose run --rm --no-deps platform setup`; the
# environment and this file win over them - see docker/entrypoint.sh
env_file:
- path: .env
required: false
volumes:
- platform-data:/data
depends_on:
db-init:
condition: service_completed_successfully
redis:
condition: service_healthy
qdrant:
condition: service_healthy
garage-init:
condition: service_completed_successfully
restart: unless-stopped
healthcheck:
# Use node for the healthcheck since the slim image has no wget/curl
test:
[
'CMD',
'node',
'-e',
"fetch('http://localhost:3000/').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 90s
db-init:
profiles: ['distro']
image: ${PLATFORM_INIT_IMAGE:-ghcr.io/chatbotkit/platform-community-init:next}
build:
context: .
dockerfile: docker/Dockerfile
target: initializer
environment:
PRISMA_DATABASE_URL: file:/data/chatbotkit.db
volumes:
- platform-data:/data
restart: 'no'
dev:
image: mcr.microsoft.com/devcontainers/javascript-node:24-bookworm
working_dir: /workspace
# @note the checkout is mounted READ-ONLY at /src and copied into the
# container, where the install lives - the host tree is never written. A
# background loop re-syncs edits every 2 seconds for hot reload.
# Provisioning (.env from the example, SQLite client and schema) happens
# here in the compose file; the application itself has no provisioning
# logic. `corepack pnpm` dispatches the exact version pinned in
# package.json's packageManager field, immune to the image's own pnpm.
command:
- bash
- -lc
- >-
command -v rsync >/dev/null || { apt-get update -qq && apt-get install -y -qq rsync >/dev/null; };
SYNC="rsync -a --delete
--exclude node_modules --exclude .next --exclude .turbo
--exclude .dev --exclude .env --exclude .pnpm-store
--exclude .git --exclude prisma/generated --exclude prisma/zod
/src/ /workspace/";
$$SYNC &&
corepack pnpm install &&
{ [ -f platform/.env ] || cp platform/.env.example platform/.env; } &&
sed -i "s|^PRISMA_DATABASE_URL=.*|PRISMA_DATABASE_URL=file:/workspace/platform/.dev/platform.db|" platform/.env &&
{ ls node_modules/.pnpm/@pothos+plugin-prisma*/node_modules/@pothos/plugin-prisma/generated.js >/dev/null 2>&1 || corepack pnpm -F @chatbotkit-dev/db db:gen; } &&
{ [ -f platform/.dev/platform.db ] || { mkdir -p platform/.dev && PRISMA_DATABASE_URL=file:/workspace/platform/.dev/platform.db corepack pnpm -F @chatbotkit-dev/db db:push; }; } &&
{ while true; do sleep 2; $$SYNC; done & } &&
corepack pnpm -F @chatbotkit/platform dev
environment:
<<: *storage-env
REDIS_URL: redis://redis:6379
QDRANT_URL: http://qdrant:6333
APP_PORT: 8080
# @note the store lives in a volume so installs stay warm across
# container recreations
NPM_CONFIG_STORE_DIR: /pnpm-store
volumes:
- .:/src:ro
- dev-workspace:/workspace
- dev-pnpm-store:/pnpm-store
ports:
- '127.0.0.1:8080:8080'
depends_on:
redis:
condition: service_healthy
qdrant:
condition: service_healthy
garage-init:
condition: service_completed_successfully
redis:
image: redis:7-alpine
command: ['redis-server', '--appendonly', 'yes']
ports:
# @note published on localhost only, so `pnpm dev` on the host can use
# this same Redis (REDIS_URL=redis://localhost:6379) without building
# anything
- '127.0.0.1:6379:6379'
volumes:
- redis-data:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
qdrant:
image: qdrant/qdrant:v1.15.1
ports:
# @note published on localhost only, so `pnpm dev` on the host can use
# this same Qdrant (QDRANT_URL=http://localhost:6333) without building
# anything
- '127.0.0.1:6333:6333'
volumes:
- qdrant-data:/qdrant/storage
healthcheck:
# @note the qdrant image ships no curl or wget, so the check is bash
# opening a TCP connection to the HTTP port
test: ['CMD-SHELL', 'bash -c ": > /dev/tcp/127.0.0.1/6333" || exit 1']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
garage:
image: dxflrs/garage:v2.1.0
environment:
# @note the 10s healthcheck logs three INFO lines per run; keep only
# warnings and errors
RUST_LOG: warn
ports:
# @note published on every interface: browsers talk to the store
# directly through presigned URLs (see x-storage-env), and `pnpm dev`
# on the host uses the same port. Same port on both sides, so the one
# endpoint works from inside the network too
- '3900:3900'
networks:
default:
aliases:
- cbk-storage.localhost
volumes:
- ./docker/garage/garage.toml:/etc/garage.toml:ro
- garage-data:/var/lib/garage
healthcheck:
# @note the image is a bare binary - no shell - so the check is the
# garage CLI talking to the daemon over RPC
test: ['CMD', '/garage', '-c', '/etc/garage.toml', 'status']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
garage-init:
# @note one-shot: provisions the single-node layout, the development
# access key and one bucket per storage scope through Garage's admin API.
# Idempotent, so it reruns harmlessly on every `up`.
image: node:24.20.0-alpine
command: ['node', '/init.mjs']
environment:
GARAGE_ADMIN_URL: http://garage:3903
GARAGE_ADMIN_TOKEN: dev-admin-token
GARAGE_S3_URL: http://garage:3900
# @note origins allowed to use presigned URLs from a browser; the URLs
# themselves are the access control
STORAGE_CORS_ORIGINS: ${STORAGE_CORS_ORIGINS:-*}
STORAGE_ACCESS_KEY_ID: GK31e57eba9df26b2e7e1b0eaa
STORAGE_SECRET_ACCESS_KEY: 9f3c1e2b8a4d5f6071829304a5b6c7d8e9f00112233445566778899aabbccdde
volumes:
- ./docker/garage/init.mjs:/init.mjs:ro
depends_on:
garage:
condition: service_healthy
restart: 'no'
volumes:
platform-data:
redis-data:
qdrant-data:
garage-data:
dev-pnpm-store:
dev-workspace: