Skip to content
Open
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
105 changes: 34 additions & 71 deletions .claude/commands/prime-proxy.md
Original file line number Diff line number Diff line change
@@ -1,112 +1,75 @@
# Prime Context: Proxy Infrastructure
# Prime Context: Proxy Infrastructure (V2)

You are now working on the **proxy infrastructure** for this system.

## Architecture Overview

This system uses a **template-based proxy generation** paradigm:
This system uses a **Traefik label-based routing** paradigm (V2):

1. **Source of Truth**: `db.yml` defines all services and their routing rules
2. **Code Generation**: Python templates generate:
- `proxy/docker-compose.yml` - Traefik stack configuration
- `proxy/traefik/traefik.yml` - Traefik static configuration
- `proxy/traefik/dynamic/*.yml` - Dynamic routing rules
3. **Zero-Downtime Deployment**:
- Stateless services (traefik, crowdsec) use `docker-rollout`
- Infrastructure services (dockerproxy, dns) restart normally
1. **Source of Truth**: Project compose files in `projects/{project}/docker-compose.yml` and `projects/{project}/traefik.yml`
2. **Code Generation**: Python scripts generate:
- `upstream/{project}/docker-compose.yml` - Service configuration with Traefik labels injected
3. **Routing**:
- Traefik automatically discovers services via Docker labels
- No separate proxy configuration files needed

## Key Components

### Traefik Stack
- **traefik**: Reverse proxy with Let's Encrypt, listens on ports 8080 (http) and 8443 (https)
- **dockerproxy**: Secure Docker socket proxy (minimal permissions)
- **dns**: DNS honeypot at 172.30.0.253 for logging
- **crowdsec**: Security plugin for threat detection

### Routing Modes
- **HTTP/HTTPS**: Domain-based routing with automatic TLS via Let's Encrypt
- **TCP**: SNI-based routing for raw TCP (databases, VPN)
- **UDP**: Port-based routing (VPN)

### Discovery Methods
1. **Dynamic Labels** (preferred): Services with `ingress.domain` get Traefik labels auto-generated
2. **Static File Routers**: Services with `ingress.hostport` or `passthrough` get static config files
### Discovery Method (V2)
All services use **Traefik Docker Labels** which are automatically injected into docker-compose files during generation.

## Critical Files to Read

**Start here** to understand the proxy system:

1. **lib/proxy.py** - Proxy management logic
- `update_proxy()` - Smart update with change detection
- `write_compose()` - Generate docker-compose.yml
- `write_routers()` - Generate dynamic routing config
- `write_config()` - Generate Traefik static config

2. **tpl/proxy/docker-compose.yml.j2** - Compose template
- Service definitions for traefik, dockerproxy, dns, crowdsec
- Healthcheck patterns
- Dependency graph

3. **tpl/proxy/traefik.yml.j2** - Traefik static config template
- Entry points (web, web-secure, tcp/udp hostports)
- Let's Encrypt configuration
- Provider settings (Docker, file)
**Start here** to understand the V2 system:

4. **tpl/proxy/routers-{http,tcp,udp}.yml.j2** - Dynamic routing templates
- Router and service definitions for static routes
- Middleware chains
1. **bin/write_artifacts.py** - Generate upstream configs with Traefik labels
- `inject_traefik_labels()` - Injects Traefik labels into services
- `write_upstream()` - Generates single project config
- `write_upstreams()` - Generates all project configs

5. **README.md** - Project documentation
- Sections: "Proxy Stack", "Service Deployment", "Routing"
2. **lib/data.py** - Project loading and validation
- `load_project()` - Loads docker-compose.yml and traefik.yml from projects/
- `validate_all()` - Validates all projects

## Key Concepts
3. **README.md** - Project documentation
- V2 Architecture section

### Stateless Services (use docker-rollout)
- **traefik**: Main reverse proxy
- **crowdsec**: Security plugin
- Can scale to 2x instances safely during rollout

### Infrastructure Services (restart normally)
- **dockerproxy**: Socket proxy (1ms startup)
- **dns**: DNS honeypot (fast restart)
- Cannot use rollout (no meaningful state)

### Healthchecks
- **dockerproxy**: `./healthcheck` binary (requires `-allowhealthcheck` flag)
- **traefik**: `traefik healthcheck` command
- All include `/tmp/drain` check for docker-rollout compatibility
## Key Concepts (V2)

### Update Flow
```
db.yml change → bin/apply.pywrite_proxies() → docker compose up -d → rollout stateless services
projects/{project}/docker-compose.yml change → itsup apply → write_upstreams() → docker compose up -d
```

### Label Injection
Traefik labels are automatically injected based on the `ingress` section in `projects/{project}/traefik.yml`.

## Common Tasks

**Regenerate proxy config**:
**Regenerate upstream configs**:
```bash
python3 bin/write-artifacts.py # Generates all configs
docker compose -f proxy/docker-compose.yml config --quiet # Validate
python3 bin/write_artifacts.py # Generates all upstream configs
```

**Update proxy with rollout**:
**Apply changes**:
```bash
bin/apply.py # Smart update with change detection
# OR manually:
dcp up traefik # Uses update_proxy() from lib/functions.sh
itsup apply # Deploy all projects
itsup apply <project> # Deploy single project
```

**Debug routing**:
- Check `proxy/traefik/dynamic/*.yml` for static routes
- `docker logs proxy-traefik-X` for dynamic label discovery
- Traefik dashboard: https://traefik.srv.instrukt.ai (if configured)
- `docker logs <project>-<service>` to see Traefik label discovery
- Check generated `upstream/{project}/docker-compose.yml` for injected labels

## Important Constraints

1. **Never modify generated files directly** - Always edit templates or db.yml
2. **Validate YAML** after template changes with `docker compose config --quiet`
3. **Only stateless services use docker-rollout** - Check `STATELESS_SERVICES` list in lib/proxy.py
4. **Hostport services need static routers** - They bypass dynamic label discovery
1. **Never modify generated files directly** - Always edit source files in `projects/{project}/`
2. **Validate YAML** after changes with `docker compose config --quiet` in the upstream directory

## Ready to Work

Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"name": "Python: write artifacts",
"type": "debugpy",
"request": "launch",
"program": "bin/write-artifacts.py",
"program": "bin/write_artifacts.py",
"console": "integratedTerminal",
"justMyCode": false,
"envFile": "${workspaceFolder}/.env",
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ make logs # Same as above
### Utilities

```bash
bin/write-artifacts.py # Regenerate proxy and upstream configs without deploying
bin/write_artifacts.py # Regenerate upstream configs without deploying
bin/backup.py # Backup upstream/ directory to S3
bin/requirements-update.sh # Update Python dependencies
```
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,7 @@ I don't want to switch folders/terminals all the time and want to keep a "projec

### Utility scripts

- `bin/write-artifacts.py`: after updating `db.yml` you can run this script to generate new artifacts.
- `bin/validate-db.py`: also ran from `bin/write-artifacts.py`
- `bin/write_artifacts.py`: after updating `db.yml` you can run this script to generate new artifacts.
- `bin/requirements-update.sh`: You may want to update requirements once in a while ;)

### Makefile
Expand Down Expand Up @@ -505,7 +504,7 @@ You can enable and configure plugins in `db.yml`. Right now we support the follo

**Step 1: generate api key**

First set `enable: true`, run `bin/write-artifacts.py`, and bring up the `crowdsec` container:
First set `enable: true`, run `bin/write_artifacts.py`, and bring up the `crowdsec` container:

```
docker compose up -d crowdsec
Expand Down
1 change: 1 addition & 0 deletions bin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# bin package
48 changes: 40 additions & 8 deletions bin/apply.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,56 @@
#!.venv/bin/python

import os
import subprocess
import sys

from dotenv import load_dotenv

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

from bin.write_artifacts import write_upstreams
from lib.data import list_projects
from lib.logging_config import setup_logging
from lib.proxy import update_proxy, write_proxies
from lib.upstream import update_upstreams, write_upstreams

load_dotenv()


def _build_docker_compose_cmd(project: str) -> list[str]:
"""Build docker compose command for deploying a project."""
upstream_dir = f"upstream/{project}"
compose_file = f"{upstream_dir}/docker-compose.yml"
return [
"docker",
"compose",
"--project-directory",
upstream_dir,
"-p",
project,
"-f",
compose_file,
"up",
"-d",
]


if __name__ == "__main__":
setup_logging()

write_proxies()
write_upstreams()

# Update with zero-downtime (auto-detects changes)
update_proxy()
update_upstreams()
# Generate all upstream configs
if not write_upstreams():
sys.exit(1)

# Deploy all upstreams
failed_projects = []
for project in list_projects():
cmd = _build_docker_compose_cmd(project)
try:
subprocess.run(cmd, check=True)
print(f"✓ {project}")
except subprocess.CalledProcessError:
print(f"✗ {project} failed")
failed_projects.append(project)

if failed_projects:
print(f"Failed projects: {', '.join(failed_projects)}")
sys.exit(1)
Loading
Loading