e4dcfc49aa
Tests / Lint and Format (push) Waiting to run
Tests / Web Node Tests (push) Waiting to run
Tests / Import Check (Python 3.11) (push) Waiting to run
Tests / Import Check (Python 3.12) (push) Waiting to run
Tests / Import Check (Python 3.13) (push) Waiting to run
Tests / Import Check (Python 3.14) (push) Waiting to run
Tests / Python Tests (Python 3.11) (push) Blocked by required conditions
Tests / Python Tests (Python 3.12) (push) Blocked by required conditions
Tests / Python Tests (Python 3.13) (push) Blocked by required conditions
Tests / Python Tests (Python 3.14) (push) Blocked by required conditions
Tests / Test Summary (push) Blocked by required conditions
169 lines
6.8 KiB
YAML
169 lines
6.8 KiB
YAML
# ============================================
|
|
# 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
|