-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
168 lines (154 loc) · 6.77 KB
/
Copy pathdocker-compose.yml
File metadata and controls
168 lines (154 loc) · 6.77 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
# ============================================
# DeepTutor Docker Compose Configuration
# ============================================
# This file provides orchestration for DeepTutor services
#
# Usage:
# Production: python scripts/docker_compose.py up -d
# Development: python scripts/docker_compose.py -f docker-compose.yml -f docker-compose.dev.yml up
# Build only: python scripts/docker_compose.py build
#
# Prerequisites:
# 1. Runtime settings are stored in ./data/user/settings (created automatically)
# 2. Configure model providers from the web Settings page or model_catalog.json
# 3. Use scripts/docker_compose.py so port mappings are rendered from system.json
#
# Local LLM (LM Studio / Ollama / vLLM):
# Use "host.docker.internal" instead of "localhost" in provider base_url fields.
# Configure provider endpoints in data/user/settings/model_catalog.json or the UI.
# ============================================
services:
# ============================================
# PocketBase — optional auth + storage sidecar
# Set integrations.pocketbase_url=http://pocketbase:8090 to activate.
# Leave integrations.pocketbase_url blank to run without PocketBase (SQLite fallback).
# ============================================
pocketbase:
image: ghcr.io/muchobien/pocketbase:latest
container_name: pocketbase
restart: unless-stopped
ports:
- "${DEEPTUTOR_DOCKER_POCKETBASE_PORT:-8090}:8090"
volumes:
# The upstream pocketbase image's entrypoint uses absolute paths
# (no /pb/ prefix): --dir=/pb_data --publicDir=/pb_public
# --hooksDir=/pb_hooks. The previous example mounted at /pb/pb_data
# and crashed on first start with "mkdir /pb_data: read-only file
# system".
- ./data/pocketbase:/pb_data
- ./data/pocketbase/public:/pb_public
- ./data/pocketbase/hooks:/pb_hooks
networks:
- deeptutor-network
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8090/api/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
# ============================================
# All-in-One DeepTutor Service
# ============================================
deeptutor:
build:
context: .
dockerfile: Dockerfile
target: production
container_name: deeptutor
restart: unless-stopped
ports:
- "${DEEPTUTOR_DOCKER_BACKEND_PORT:-8001}:${DEEPTUTOR_DOCKER_BACKEND_PORT:-8001}"
- "${DEEPTUTOR_DOCKER_FRONTEND_PORT:-3782}:${DEEPTUTOR_DOCKER_FRONTEND_PORT:-3782}"
volumes:
# Everything DeepTutor persists lives under ./data — one tree to mount
# and back up: admin workspace + runtime settings (data/user), per-user
# workspaces (data/users), partners (data/partners), accounts/grants/
# audit (data/system), knowledge bases, memory.
# The sandbox-runner deliberately gets only the *workspace* subtrees of
# this volume (the mount contract below) — never data/system or
# data/user/settings, which hold auth state and provider API keys.
- ./data:/app/data
environment:
# Route untrusted shell exec to the runner sidecar (SYSTEM isolation).
# When this is set, the main app NEVER runs untrusted code in its own
# process — see deeptutor/services/sandbox/config.py:build_backend().
# On bare-metal / macOS deployments where the runner is not started, leave
# this unset: the app degrades to bwrap (Linux, if installed) or, only when
# DEEPTUTOR_SANDBOX_ALLOW_SUBPROCESS=1, a restricted subprocess.
- DEEPTUTOR_SANDBOX_RUNNER_URL=http://sandbox-runner:8900
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:${DEEPTUTOR_DOCKER_BACKEND_PORT:-8001}/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
depends_on:
pocketbase:
condition: service_healthy
sandbox-runner:
condition: service_healthy
networks:
- deeptutor-network
# ============================================
# Sandbox Runner Sidecar
# ============================================
# Executes untrusted shell commands in an isolated, least-privileged
# container so the main app never has to. Rationale for the sidecar shape:
# * the main `deeptutor` container keeps minimal privileges and does NOT
# mount the docker socket or run untrusted code in its own process;
# * a sandbox escape here lands in a stripped, unprivileged container with
# no app secrets, not in the main app.
# Not exposed to the host: it is reachable only on the internal
# deeptutor-network at http://sandbox-runner:8900.
sandbox-runner:
build:
context: .
dockerfile: Dockerfile.runner
container_name: deeptutor-sandbox-runner
restart: unless-stopped
# Share the task workspaces at the SAME paths as the main app so the mount
# contract holds: a request with host_path == sandbox_path under
# /app/data/user/workspace (admin) or /app/data/users/<uid> (per-user)
# is already visible here, no per-command mounting needed.
# Scope note: every command in this container shares one filesystem view
# (per-command isolation is a roadmap item), so the mounts are kept as
# narrow as the exec feature allows — workspace subtrees only. Secrets
# (data/system, data/user/settings) never enter this container; the
# residual exposure is cross-user visibility *between user workspaces*,
# acceptable for the invite-only trust posture and enforced no further.
volumes:
- ./data/user/workspace:/app/data/user/workspace
- ./data/users:/app/data/users
# No `ports:` — intentionally not published to the host.
# ----- Security hardening -----
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
# Read-only root filesystem: the runner only needs to read its own code and
# write under the shared volume (and scratch space). Give it tmpfs for the
# paths shell tooling expects to be writable (/tmp, $HOME). If a workload
# needs more writable scratch, relax read_only rather than widening tmpfs.
read_only: true
tmpfs:
- /tmp
- /home/runner
pids_limit: 256
mem_limit: 1g
# cgroup-enforced RSS cap is the authoritative memory backstop; the
# per-command RLIMIT_AS in server.py is a cheaper secondary guard.
healthcheck:
test: ["CMD", "python", "-c",
"import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8900/health',timeout=3).status==200 else 1)"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
networks:
- deeptutor-network
# ============================================
# Networks
# ============================================
networks:
deeptutor-network:
driver: bridge