diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5525403 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,22 @@ +# Secrets — never bake into the image +.env + +# Version control +.git/ +.gitignore + +# Python build artefacts +__pycache__/ +*.pyc +*.pyo +.pytest_cache/ + +# Tests — not needed at runtime +tests/ + +# Persistent data — mounted as a volume at runtime +data/ + +# Tool configs — not needed in the container +.claude/ +.gemini/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7ef07ed --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.13-slim + +# Install uv +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/ + +WORKDIR /app + +# Install Python dependencies — cached independently of application code +COPY pyproject.toml uv.lock ./ +RUN uv sync --frozen --no-dev --no-install-project + +# Copy application source +COPY src/ ./src/ +COPY chat_app.py main.py chainlit.toml ./ +COPY public/ ./public/ + +# Make the venv's binaries (chainlit, python, etc.) available directly +ENV PATH="/app/.venv/bin:$PATH" + +# data/ and openspec/ are expected to be mounted at runtime diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..15d6385 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +services: + + # Web chat interface — Chainlit served at http://localhost:8000 + web: + build: . + command: chainlit run chat_app.py --host 0.0.0.0 --port 8000 + ports: + - "8000:8000" + env_file: .env + volumes: + # Persistent storage for conversations and completed registrations + - ./data:/app/data + # Knowledge-base markdown files — edit on the host, no rebuild needed + - ./openspec:/app/openspec:ro + restart: unless-stopped + + # Email polling agent — checks the inbox every POLL_INTERVAL seconds + email-worker: + build: . + command: python main.py + env_file: .env + volumes: + # Shares the same data directory as the web service + - ./data:/app/data + - ./openspec:/app/openspec:ro + restart: unless-stopped