Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Agent Guidelines for currents-dev-docker

## Docker Compose Patterns

### Variable Naming

- **`DC_` prefix**: Use for docker-compose-only variables (not passed to containers)
- `DC_MONGODB_PORT`, `DC_CURRENTS_IMAGE_TAG`, `DC_REDIS_VOLUME`
- **No prefix**: For app config variables that containers need
- `MONGODB_PASSWORD`, `APP_BASE_URL`, `CLICKHOUSE_CURRENTS_PASSWORD`

### Port Configuration

- Default **database ports to localhost-only**: `${DC_MONGODB_PORT:-127.0.0.1:27017}:27017`
- Default **application ports to all interfaces**: `${DC_API_PORT:-4000}:4000`
- Don't use `expose:` - it has no functional effect

### Image Configuration

- **Currents services**: Use repository + tag pattern
```yaml
image: ${DC_CURRENTS_IMAGE_REPOSITORY:-currents-}api:${DC_CURRENTS_IMAGE_TAG:-dev}
```
- **Infrastructure services**: Use full image reference
```yaml
image: ${DC_MONGODB_IMAGE:-mongo:8.2.3}
```

### Initialization

- Use `command` instead of `entrypoint` when you want to keep the default Docker entrypoint behavior
- For multi-line scripts in YAML, use array format with block scalar to avoid parsing issues with colons:
```yaml
post_start:
- command:
- bash
- -c
- |
echo "script here"
```

## MongoDB Specifics

- **Replica sets + auth require keyFile** - even single-node replica sets
- **Change streams require replica sets** - can't use standalone MongoDB
- **Connection strings need `authSource=admin`** for root users created by `MONGO_INITDB_ROOT_*`
- Use localhost exception for initial user creation when auth is enabled

## Security Patterns

- **Never default passwords** - require them to be set, generate in setup.sh
- **Keep credentials out of healthcheck commands** - they show in `docker inspect`
- Use variable interpolation for derived URLs: `API_URL=${APP_BASE_URL}/v1`

## Project Structure

- Templates live in `on-prem/templates/compose.*.yml`
- `generate-compose.sh` merges templates into final compose files
- `setup.sh` generates secrets using `generate-secrets.sh`
- `.env.example` documents all configurable variables
23 changes: 23 additions & 0 deletions on-prem/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,29 @@
# DC_RUSTFS_S3_PORT=9000 # RustFS S3 API (default: all interfaces)
# DC_RUSTFS_CONSOLE_PORT=9001 # RustFS Console (default: all interfaces)

# =============================================================================
# Traefik TLS Termination (Optional)
# =============================================================================
# When using the traefik profile, configure TLS termination with a wildcard cert.
# Certificate handling:
# - By default (TRAEFIK_GENERATE_TEMP_CERTS=false): Place wildcard.crt and wildcard.key in data/traefik/certs/
# - For temporary auto-generated certs: Set TRAEFIK_GENERATE_TEMP_CERTS=true (Traefik generates self-signed certs in memory)
#
# DC_TRAEFIK_IMAGE=traefik:v3.3 # Traefik image
# DC_TRAEFIK_HTTP_PORT=80 # HTTP port (redirects to HTTPS)
# DC_TRAEFIK_HTTPS_PORT=443 # HTTPS port
# DC_TRAEFIK_CERTS_DIR=./data/traefik/certs # Certificate directory
# TRAEFIK_GENERATE_TEMP_CERTS=false # Generate temporary certs (set to true for Traefik to auto-generate)
# DC_TRAEFIK_CONFIG_DIR=./data/traefik/config # Directory for custom Traefik config files
# DC_TRAEFIK_DYNAMIC_CONFIG_PATH=/etc/traefik/dynamic.yml # Path to dynamic config (default uses built-in config, or set to /traefik-config/dynamic.yml for custom)

# Domain configuration for Traefik routing
# TRAEFIK_DOMAIN=example.com # Base domain for wildcard cert
# TRAEFIK_API_SUBDOMAIN=currents-app # Subdomain for API/Dashboard
# TRAEFIK_DIRECTOR_SUBDOMAIN=currents-record # Subdomain for Director
# TRAEFIK_STORAGE_SUBDOMAIN=currents-storage # Subdomain for RustFS S3 API
# TRAEFIK_ENABLE_STORAGE=false # Enable storage routing (automatically set to true when rustfs is included)

# =============================================================================
# URL Configuration
# =============================================================================
Expand Down
101 changes: 101 additions & 0 deletions on-prem/docker-compose.cache.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Generated by: ./scripts/generate-compose.sh
# Profile: cache - Cache (redis)
#
# Includes Traefik for TLS termination (opt-in with: --profile tls)
# Enable with: docker compose --profile tls up

name: currents_cache

Expand Down Expand Up @@ -101,6 +104,48 @@ services:
redis:
condition: service_started
required: false
traefik:
hostname: traefik
image: ${DC_TRAEFIK_IMAGE:-traefik:v3.3}
restart: unless-stopped
profiles:
- tls
environment:
TRAEFIK_ENABLE_STORAGE: ${TRAEFIK_ENABLE_STORAGE:-false}
TRAEFIK_GENERATE_TEMP_CERTS: ${TRAEFIK_GENERATE_TEMP_CERTS:-false}
ports:
- ${DC_TRAEFIK_HTTP_PORT:-80}:80
- ${DC_TRAEFIK_HTTPS_PORT:-443}:443
networks:
- default
volumes:
- ${DC_TRAEFIK_CERTS_DIR:-./data/traefik/certs}:/certs
- ${DC_TRAEFIK_CONFIG_DIR:-./data/traefik/config}:/traefik-config:ro
command:
# Entrypoints
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# HTTP to HTTPS redirect
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
# TLS configuration (file-based certificates)
- --entrypoints.websecure.http.tls=true
# File provider for dynamic configuration
- --providers.file.filename=${DC_TRAEFIK_DYNAMIC_CONFIG_PATH:-/etc/traefik/dynamic.yml}
- --providers.file.watch=true
# Enable API for healthcheck (insecure mode, internal only)
- --api.insecure=true
- --api.dashboard=false
# Health check endpoint
- --ping=true
configs:
- source: traefik_dynamic
target: /etc/traefik/dynamic.yml
healthcheck:
test: ['CMD', 'traefik', 'healthcheck', '--ping']
interval: 10s
timeout: 5s
retries: 3
redis:
hostname: redis
image: ${DC_REDIS_IMAGE:-redis/redis-stack-server:7.4.0-v8}
Expand All @@ -113,3 +158,59 @@ services:
- ${DC_REDIS_VOLUME:-./data/redis}:/data
networks:
default: null
configs:
traefik_dynamic:
content: |
# Traefik dynamic configuration (supports Go templating with sprig)
{{- if ne (env "TRAEFIK_GENERATE_TEMP_CERTS") "true" }}
tls:
certificates:
- certFile: /certs/wildcard.crt
keyFile: /certs/wildcard.key
{{- end }}

http:
routers:
# API / Dashboard service
api:
rule: "Host(`${TRAEFIK_API_SUBDOMAIN:-currents-app}.${TRAEFIK_DOMAIN}`)"
service: api
entryPoints:
- websecure
tls: {}

# Director service (record API)
director:
rule: "Host(`${TRAEFIK_DIRECTOR_SUBDOMAIN:-currents-record}.${TRAEFIK_DOMAIN}`)"
service: director
entryPoints:
- websecure
tls: {}

{{- if eq (env "TRAEFIK_ENABLE_STORAGE") "true" }}
# RustFS S3 API (conditionally enabled)
storage:
rule: "Host(`${TRAEFIK_STORAGE_SUBDOMAIN:-currents-storage}.${TRAEFIK_DOMAIN}`)"
service: rustfs
entryPoints:
- websecure
tls: {}
{{- end }}

services:
api:
loadBalancer:
servers:
- url: "http://api:4000"

director:
loadBalancer:
servers:
- url: "http://director:1234"

{{- if eq (env "TRAEFIK_ENABLE_STORAGE") "true" }}
rustfs:
loadBalancer:
servers:
- url: "http://rustfs:9000"
{{- end }}
100 changes: 100 additions & 0 deletions on-prem/docker-compose.database.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Generated by: ./scripts/generate-compose.sh
# Profile: database - Database services (redis, mongodb, clickhouse)
#
# Includes Traefik for TLS termination (opt-in with: --profile tls)
# Enable with: docker compose --profile tls up

name: currents_database

Expand Down Expand Up @@ -137,6 +140,48 @@ services:
redis:
condition: service_started
required: false
traefik:
hostname: traefik
image: ${DC_TRAEFIK_IMAGE:-traefik:v3.3}
restart: unless-stopped
profiles:
- tls
environment:
TRAEFIK_ENABLE_STORAGE: ${TRAEFIK_ENABLE_STORAGE:-false}
TRAEFIK_GENERATE_TEMP_CERTS: ${TRAEFIK_GENERATE_TEMP_CERTS:-false}
ports:
- ${DC_TRAEFIK_HTTP_PORT:-80}:80
- ${DC_TRAEFIK_HTTPS_PORT:-443}:443
networks:
- default
volumes:
- ${DC_TRAEFIK_CERTS_DIR:-./data/traefik/certs}:/certs
- ${DC_TRAEFIK_CONFIG_DIR:-./data/traefik/config}:/traefik-config:ro
command:
# Entrypoints
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# HTTP to HTTPS redirect
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
# TLS configuration (file-based certificates)
- --entrypoints.websecure.http.tls=true
# File provider for dynamic configuration
- --providers.file.filename=${DC_TRAEFIK_DYNAMIC_CONFIG_PATH:-/etc/traefik/dynamic.yml}
- --providers.file.watch=true
# Enable API for healthcheck (insecure mode, internal only)
- --api.insecure=true
- --api.dashboard=false
# Health check endpoint
- --ping=true
configs:
- source: traefik_dynamic
target: /etc/traefik/dynamic.yml
healthcheck:
test: ['CMD', 'traefik', 'healthcheck', '--ping']
interval: 10s
timeout: 5s
retries: 3
clickhouse:
hostname: clickhouse
image: ${DC_CLICKHOUSE_IMAGE:-clickhouse/clickhouse-server:25.8}
Expand Down Expand Up @@ -231,6 +276,61 @@ services:
networks:
default: null
configs:
traefik_dynamic:
content: |
# Traefik dynamic configuration (supports Go templating with sprig)
{{- if ne (env "TRAEFIK_GENERATE_TEMP_CERTS") "true" }}
tls:
certificates:
- certFile: /certs/wildcard.crt
keyFile: /certs/wildcard.key
{{- end }}

http:
routers:
# API / Dashboard service
api:
rule: "Host(`${TRAEFIK_API_SUBDOMAIN:-currents-app}.${TRAEFIK_DOMAIN}`)"
service: api
entryPoints:
- websecure
tls: {}

# Director service (record API)
director:
rule: "Host(`${TRAEFIK_DIRECTOR_SUBDOMAIN:-currents-record}.${TRAEFIK_DOMAIN}`)"
service: director
entryPoints:
- websecure
tls: {}

{{- if eq (env "TRAEFIK_ENABLE_STORAGE") "true" }}
# RustFS S3 API (conditionally enabled)
storage:
rule: "Host(`${TRAEFIK_STORAGE_SUBDOMAIN:-currents-storage}.${TRAEFIK_DOMAIN}`)"
service: rustfs
entryPoints:
- websecure
tls: {}
{{- end }}

services:
api:
loadBalancer:
servers:
- url: "http://api:4000"

director:
loadBalancer:
servers:
- url: "http://director:1234"

{{- if eq (env "TRAEFIK_ENABLE_STORAGE") "true" }}
rustfs:
loadBalancer:
servers:
- url: "http://rustfs:9000"
{{- end }}
clickhouse_users:
content: |
<?xml version="1.0"?>
Expand Down
Loading