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
4 changes: 1 addition & 3 deletions .github/actions/e2e-ready/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ runs:

- name: Use test environment config
shell: bash
run: |
cp backend/config.test.toml backend/config.toml
cp backend/secrets.example.toml backend/secrets.toml
run: cp backend/config.test.toml backend/config.toml

- name: Pre-pull test runtime images into K3s
shell: bash
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ jobs:
- name: Install backend dependencies
run: cd backend && uv sync --frozen --no-dev

- name: Set up config for OpenAPI generation
run: cp backend/secrets.example.toml backend/secrets.toml

- name: Generate OpenAPI spec
run: ./deploy.sh openapi

Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/release-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,12 @@ jobs:
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
MONGO_USER: ${{ secrets.MONGO_USER }}
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
envs: GHCR_TOKEN,GHCR_USER,IMAGE_TAG,GRAFANA_ADMIN_USER,GRAFANA_ADMIN_PASSWORD,MAILJET_API_KEY,MAILJET_SECRET_KEY,MAILJET_FROM_ADDRESS,MAILJET_HOST,GRAFANA_ALERT_RECIPIENTS,REDIS_PASSWORD,MONGO_USER,MONGO_PASSWORD
envs: GHCR_TOKEN,GHCR_USER,IMAGE_TAG,GRAFANA_ADMIN_USER,GRAFANA_ADMIN_PASSWORD,MAILJET_API_KEY,MAILJET_SECRET_KEY,MAILJET_FROM_ADDRESS,MAILJET_HOST,GRAFANA_ALERT_RECIPIENTS,REDIS_PASSWORD,MONGO_USER,MONGO_PASSWORD,SECRET_KEY
command_timeout: 10m
script: |
set -e
Expand All @@ -148,6 +149,10 @@ jobs:

export IMAGE_TAG="$IMAGE_TAG"
export COMPOSE_PROFILES=observability
export SECRET_KEY="$SECRET_KEY"
export MONGO_USER="$MONGO_USER"
export MONGO_PASSWORD="$MONGO_PASSWORD"
export REDIS_PASSWORD="$REDIS_PASSWORD"
export GRAFANA_ROOT_URL="https://grafana.integr8scode.cc/"
export GRAFANA_ADMIN_USER="$GRAFANA_ADMIN_USER"
export GRAFANA_ADMIN_PASSWORD="$GRAFANA_ADMIN_PASSWORD"
Expand All @@ -156,9 +161,6 @@ jobs:
export MAILJET_FROM_ADDRESS="$MAILJET_FROM_ADDRESS"
export GF_SMTP_HOST="$MAILJET_HOST"
export GRAFANA_ALERT_RECIPIENTS="$GRAFANA_ALERT_RECIPIENTS"
export REDIS_PASSWORD="$REDIS_PASSWORD"
export MONGO_USER="$MONGO_USER"
export MONGO_PASSWORD="$MONGO_PASSWORD"
docker compose pull
docker compose up -d --remove-orphans --no-build --wait --wait-timeout 180

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/stack-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ jobs:
uv python install 3.12
uv sync --frozen --group test --no-dev

- name: Set up config
run: cp backend/secrets.example.toml backend/secrets.toml

- name: Run unit tests
timeout-minutes: 5
run: |
Expand Down
14 changes: 7 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,22 +438,22 @@ class ExecutionService:
## Backend: Settings (TOML — No Env Vars)

```python
# WRONG — never use environment variables
# WRONG — never read config directly from env vars in application code
DATABASE_URL = os.getenv("DATABASE_URL")

# CORRECT — inject via DI
# CORRECT — inject via DI (Settings reads env vars for secrets internally)
settings: FromDishka[Settings]
```

Config load order (each overrides previous):
1. `config.toml` — base settings (committed)
2. `secrets.toml` — credentials (gitignored, never committed)
3. `config.<worker>.toml` — per-worker overrides (optional)
2. `config.<worker>.toml` — per-worker overrides (optional)
3. Environment variables for secrets: `SECRET_KEY`, `MONGO_USER`, `MONGO_PASSWORD`, `REDIS_PASSWORD`

```python
Settings() # config.toml + secrets.toml
Settings(config_path="config.test.toml") # tests
Settings(override_path="config.saga-orchestrator.toml") # worker
Settings() # config.toml + env vars
Settings(config_path="config.test.toml") # tests + env vars
Settings(override_path="config.saga-orchestrator.toml") # worker + env vars
```

`Settings` uses `model_config = ConfigDict(extra="forbid")` — unknown keys are errors.
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,10 @@ things safe and efficient. You'll get the results back in no time.
```bash
git clone https://github.com/HardMax71/Integr8sCode.git
cd Integr8sCode
cp backend/secrets.example.toml backend/secrets.toml
./deploy.sh dev
```

The `secrets.toml` file holds credentials and is gitignored. The example template has working development defaults.
Secrets (MongoDB, Redis, JWT) come from environment variables with dev defaults — zero config needed for local development. For production, set `SECRET_KEY`, `MONGO_USER`, `MONGO_PASSWORD`, `REDIS_PASSWORD` as env vars.

### Verify

Expand Down
29 changes: 16 additions & 13 deletions backend/app/settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
import tomllib
from pathlib import Path
from typing import Literal
from urllib.parse import quote

Expand All @@ -11,19 +11,19 @@


class Settings(BaseModel):
"""Application settings loaded from TOML configuration files.
"""Application settings loaded from TOML + environment variables.

All config is read from TOML — no environment variables, no .env files.
Non-secret config comes from TOML files; secrets come from env vars with dev defaults.

Load order (each layer overrides the previous):
1. config_path — base settings (committed to git)
2. secrets_path — sensitive overrides (gitignored, mounted from K8s Secret in prod)
3. override_path — per-worker service overrides (TRACING_SERVICE_NAME, etc.)
2. override_path — per-worker service overrides (TRACING_SERVICE_NAME, etc.)
3. Environment variables for secrets: SECRET_KEY, MONGO_USER, MONGO_PASSWORD, REDIS_PASSWORD

Usage:
Settings() # config.toml + secrets
Settings(config_path="config.test.toml") # test config (has own secrets)
Settings(override_path="config.saga-orchestrator.toml") # base + secrets + worker
Settings() # config.toml + env vars
Settings(config_path="config.test.toml") # test config + env vars
Settings(override_path="config.saga-orchestrator.toml") # base + worker + env vars
"""

model_config = ConfigDict(extra="forbid")
Expand All @@ -32,16 +32,19 @@ def __init__(
self,
config_path: str = "config.toml",
override_path: str | None = None,
secrets_path: str = "secrets.toml",
) -> None:
with open(config_path, "rb") as f:
data = tomllib.load(f)
if Path(secrets_path).is_file():
with open(secrets_path, "rb") as f:
data |= tomllib.load(f)
if override_path:
with open(override_path, "rb") as f:
data |= tomllib.load(f)
for key, default in (
("SECRET_KEY", "CHANGE_ME_min_32_chars_long_!!!!"),
("MONGO_USER", "root"),
("MONGO_PASSWORD", "rootpassword"),
("REDIS_PASSWORD", "redispassword"),
):
data[key] = os.environ.get(key) or data.get(key) or default
if data.get("MONGO_USER") and data.get("MONGO_PASSWORD"):
user = quote(data["MONGO_USER"], safe="")
password = quote(data["MONGO_PASSWORD"], safe="")
Expand Down Expand Up @@ -176,7 +179,7 @@ def __init__(
DEVELOPMENT_MODE: bool = False
SECURE_COOKIES: bool = True

# CORS allowed origins (overridable via config.toml / secrets.toml)
# CORS allowed origins (overridable via config.toml)
CORS_ORIGINS: list[str] = Field(default_factory=lambda: [
"https://localhost:5001",
"https://127.0.0.1:5001",
Expand Down
2 changes: 1 addition & 1 deletion backend/config.test.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Integr8sCode backend test configuration
# Differences from config.toml: lower timeouts, faster bcrypt, relaxed rate limits
# Secrets (SECRET_KEY, MONGODB_URL) live in secrets.toml — use secrets.test.toml in CI.
# Secrets come from environment variables with dev defaults (see settings.py).

ENVIRONMENT = "test"
PROJECT_NAME = "integr8scode"
Expand Down
6 changes: 3 additions & 3 deletions backend/config.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# --8<-- [start:core]
# Integr8sCode backend configuration (development defaults).
# Secrets (SECRET_KEY, MONGO_USER, MONGO_PASSWORD) live in secrets.toml (gitignored).
# Production: mount secrets.toml from a Kubernetes Secret or generate in CI.
# See secrets.example.toml for the required keys.
# Secrets (SECRET_KEY, MONGO_USER, MONGO_PASSWORD, REDIS_PASSWORD) come from
# environment variables with dev defaults — see Settings.__init__() in settings.py.
# Production: set env vars via docker-compose or GitHub Actions secrets.

PROJECT_NAME = "integr8scode"
DATABASE_NAME = "integr8scode_db"
Expand Down
24 changes: 0 additions & 24 deletions backend/secrets.example.toml

This file was deleted.

38 changes: 25 additions & 13 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
x-backend-secrets: &backend-secrets
SECRET_KEY: ${SECRET_KEY:-CHANGE_ME_min_32_chars_long_!!!!}
MONGO_USER: ${MONGO_USER:-root}
MONGO_PASSWORD: ${MONGO_PASSWORD:-rootpassword}
REDIS_PASSWORD: ${REDIS_PASSWORD:-redispassword}

services:
# Shared base image for all Python backend services
base:
Expand Down Expand Up @@ -113,7 +119,6 @@ services:
- ./backend/certs:/app/certs:ro
- ./backend/config.test.toml:/app/config.test.toml:ro
- ./backend/config.toml:/app/config.toml:ro
- ./backend/secrets.toml:/app/secrets.toml:ro
- shared_ca:/shared_ca:ro
- ./backend/kubeconfig.yaml:/app/kubeconfig.yaml:ro
ports:
Expand All @@ -123,9 +128,10 @@ services:
- app-network
container_name: backend
environment:
- WEB_CONCURRENCY=${WEB_CONCURRENCY:-2}
- WEB_THREADS=${WEB_THREADS:-4}
- WEB_TIMEOUT=${WEB_TIMEOUT:-60}
<<: *backend-secrets
WEB_CONCURRENCY: ${WEB_CONCURRENCY:-2}
WEB_THREADS: ${WEB_THREADS:-4}
WEB_TIMEOUT: ${WEB_TIMEOUT:-60}
extra_hosts:
- "host.docker.internal:host-gateway"
mem_limit: 768m
Expand Down Expand Up @@ -301,11 +307,11 @@ services:
mongo:
condition: service_healthy
environment:
- DEFAULT_USER_PASSWORD=${DEFAULT_USER_PASSWORD:-user123}
- ADMIN_USER_PASSWORD=${ADMIN_USER_PASSWORD:-admin123}
<<: *backend-secrets
DEFAULT_USER_PASSWORD: ${DEFAULT_USER_PASSWORD:-user123}
ADMIN_USER_PASSWORD: ${ADMIN_USER_PASSWORD:-admin123}
volumes:
- ./backend/config.toml:/app/config.toml:ro
- ./backend/secrets.toml:/app/secrets.toml:ro
command: ["python", "-m", "scripts.seed_users"]
networks:
- app-network
Expand All @@ -322,12 +328,13 @@ services:
condition: service_healthy
mongo:
condition: service_started
environment:
<<: *backend-secrets
volumes:
- ./backend/app:/app/app:ro
- ./backend/workers:/app/workers:ro
- ./backend/kubeconfig.yaml:/app/kubeconfig.yaml:ro
- ./backend/config.toml:/app/config.toml:ro
- ./backend/secrets.toml:/app/secrets.toml:ro
- ./backend/config.k8s-worker.toml:/app/config.k8s-worker.toml:ro
networks:
- app-network
Expand All @@ -343,12 +350,13 @@ services:
depends_on:
kafka:
condition: service_healthy
environment:
<<: *backend-secrets
volumes:
- ./backend/app:/app/app:ro
- ./backend/workers:/app/workers:ro
- ./backend/kubeconfig.yaml:/app/kubeconfig.yaml:ro
- ./backend/config.toml:/app/config.toml:ro
- ./backend/secrets.toml:/app/secrets.toml:ro
- ./backend/config.pod-monitor.toml:/app/config.pod-monitor.toml:ro
networks:
- app-network
Expand All @@ -366,12 +374,13 @@ services:
condition: service_healthy
mongo:
condition: service_started
environment:
<<: *backend-secrets
volumes:
- ./backend/app:/app/app:ro
- ./backend/workers:/app/workers:ro
- ./backend/kubeconfig.yaml:/app/kubeconfig.yaml:ro
- ./backend/config.toml:/app/config.toml:ro
- ./backend/secrets.toml:/app/secrets.toml:ro
- ./backend/config.result-processor.toml:/app/config.result-processor.toml:ro
networks:
- app-network
Expand All @@ -389,11 +398,12 @@ services:
condition: service_healthy
mongo:
condition: service_started
environment:
<<: *backend-secrets
volumes:
- ./backend/app:/app/app:ro
- ./backend/workers:/app/workers:ro
- ./backend/config.toml:/app/config.toml:ro
- ./backend/secrets.toml:/app/secrets.toml:ro
- ./backend/config.saga-orchestrator.toml:/app/config.saga-orchestrator.toml:ro
networks:
- app-network
Expand Down Expand Up @@ -452,11 +462,12 @@ services:
condition: service_healthy
mongo:
condition: service_started
environment:
<<: *backend-secrets
volumes:
- ./backend/app:/app/app:ro
- ./backend/workers:/app/workers:ro
- ./backend/config.toml:/app/config.toml:ro
- ./backend/secrets.toml:/app/secrets.toml:ro
- ./backend/config.event-replay.toml:/app/config.event-replay.toml:ro
networks:
- app-network
Expand All @@ -473,11 +484,12 @@ services:
condition: service_healthy
mongo:
condition: service_started
environment:
<<: *backend-secrets
volumes:
- ./backend/app:/app/app:ro
- ./backend/workers:/app/workers:ro
- ./backend/config.toml:/app/config.toml:ro
- ./backend/secrets.toml:/app/secrets.toml:ro
- ./backend/config.dlq-processor.toml:/app/config.dlq-processor.toml:ro
networks:
- app-network
Expand Down
3 changes: 1 addition & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ The full stack includes a Svelte frontend, FastAPI backend, MongoDB, Redis, Kafk
```bash
git clone https://github.com/HardMax71/Integr8sCode.git
cd Integr8sCode
cp backend/secrets.example.toml backend/secrets.toml
./deploy.sh dev
```

The `secrets.toml` file holds credentials (`SECRET_KEY`, `MONGODB_URL`) and is gitignored. The example template ships with working development defaults, so copying it is all you need for local use.
Secrets (JWT key, MongoDB and Redis credentials) are read from environment variables with dev defaults baked in — no config files to copy for local development.

Wait for the services to come up. You can watch progress with `docker compose logs -f` in another terminal. When you see the backend responding to health checks, you're ready.

Expand Down
2 changes: 1 addition & 1 deletion docs/operations/cicd.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ This action finalizes the environment after boot tasks complete:
1. **Finalize k3s** — copies kubeconfig, rewrites the API server address to `host.docker.internal` so containers
inside docker compose can reach the k3s API server, creates the `integr8scode` namespace
2. **Start cert-generator** in the background
3. **Copy test config** — uses `config.test.toml` and `secrets.example.toml`
3. **Copy test config** — uses `config.test.toml` (secrets come from env var defaults)
4. **Wait for image pull and infra** — blocks until the background pull completes and checks the exit code from
`/tmp/infra-pull.exit`, failing fast if the background process had errors
5. **Start compose stack** with `docker compose up -d --no-build`
Expand Down
2 changes: 1 addition & 1 deletion docs/operations/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ docker compose logs backend
| Issue | Cause | Solution |
|-----------------------|-----------------------------------|---------------------------------------------------|
| Unknown topic errors | Kafka not ready or wrong prefix | Check `docker compose logs kafka` |
| MongoDB auth errors | Password mismatch | Verify `secrets.toml` matches compose env vars |
| MongoDB auth errors | Password mismatch | Verify `MONGO_USER`/`MONGO_PASSWORD` env vars match MongoDB init |
| Worker crash loop | Config file missing | Ensure `config.<worker>.toml` exists |

### Kafka topic debugging
Expand Down
Loading
Loading