services: image: swiftdeploy-api-go:latest port: 3000 mode: stable app_version: "1.0.0" nginx: image: nginxinc/nginx-unprivileged:stable-alpine port: 8080 proxy_timeout: 30s opa: image: openpolicyagent/opa:latest port: 8181 policy: data_file: policy-data/thresholds.json decision_timeout_seconds: 5
services: image: swiftdeploy-api-go:latest port: 3000 mode: stable app_version: "1.0.0" nginx: image: nginxinc/nginx-unprivileged:stable-alpine port: 8080 proxy_timeout: 30s opa: image: openpolicyagent/opa:latest port: 8181 policy: data_file: policy-data/thresholds.json decision_timeout_seconds: 5
services: image: swiftdeploy-api-go:latest port: 3000 mode: stable app_version: "1.0.0" nginx: image: nginxinc/nginx-unprivileged:stable-alpine port: 8080 proxy_timeout: 30s opa: image: openpolicyagent/opa:latest port: 8181 policy: data_file: policy-data/thresholds.json decision_timeout_seconds: 5
./swiftdeploy build
./swiftdeploy init
./swiftdeploy validate
./swiftdeploy deploy
./swiftdeploy promote canary
./swiftdeploy promote stable
./swiftdeploy status 5
./swiftdeploy audit
./swiftdeploy teardown --clean
./swiftdeploy build
./swiftdeploy init
./swiftdeploy validate
./swiftdeploy deploy
./swiftdeploy promote canary
./swiftdeploy promote stable
./swiftdeploy status 5
./swiftdeploy audit
./swiftdeploy teardown --clean
./swiftdeploy build
./swiftdeploy init
./swiftdeploy validate
./swiftdeploy deploy
./swiftdeploy promote canary
./swiftdeploy promote stable
./swiftdeploy status 5
./swiftdeploy audit
./swiftdeploy teardown --clean
curl -sS "http://127.0.0.1:8080/metrics" | grep -E "http_requests_total|http_request_duration_seconds|app_uptime_seconds|app_mode|chaos_active" | head
curl -sS "http://127.0.0.1:8080/metrics" | grep -E "http_requests_total|http_request_duration_seconds|app_uptime_seconds|app_mode|chaos_active" | head
curl -sS "http://127.0.0.1:8080/metrics" | grep -E "http_requests_total|http_request_duration_seconds|app_uptime_seconds|app_mode|chaos_active" | head
curl -sS -X POST "http://127.0.0.1:8181/v1/data/policy/infrastructure/decision" \ -H "Content-Type: application/json" \ -d '{"input":{"context":"pre-deploy","disk_free_gb":50,"cpu_load":0.3}}'
curl -sS -X POST "http://127.0.0.1:8181/v1/data/policy/infrastructure/decision" \ -H "Content-Type: application/json" \ -d '{"input":{"context":"pre-deploy","disk_free_gb":50,"cpu_load":0.3}}'
curl -sS -X POST "http://127.0.0.1:8181/v1/data/policy/infrastructure/decision" \ -H "Content-Type: application/json" \ -d '{"input":{"context":"pre-deploy","disk_free_gb":50,"cpu_load":0.3}}'
curl -sS -X POST "http://127.0.0.1:8181/v1/data/policy/canary/decision" \ -H "Content-Type: application/json" \ -d '{"input":{"context":"pre-promote","window_seconds":30,"error_rate":0.001,"p99_latency_ms":100}}'
curl -sS -X POST "http://127.0.0.1:8181/v1/data/policy/canary/decision" \ -H "Content-Type: application/json" \ -d '{"input":{"context":"pre-promote","window_seconds":30,"error_rate":0.001,"p99_latency_ms":100}}'
curl -sS -X POST "http://127.0.0.1:8181/v1/data/policy/canary/decision" \ -H "Content-Type: application/json" \ -d '{"input":{"context":"pre-promote","window_seconds":30,"error_rate":0.001,"p99_latency_ms":100}}'
curl -sS -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:8080/v1/data/policy/infrastructure/decision"
curl -sS -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:8080/v1/data/policy/infrastructure/decision"
curl -sS -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:8080/v1/data/policy/infrastructure/decision"
./swiftdeploy teardown
./swiftdeploy deploy
./swiftdeploy teardown
./swiftdeploy deploy
./swiftdeploy teardown
./swiftdeploy deploy
./swiftdeploy promote canary
curl -sS -X POST "http://127.0.0.1:8080/chaos" \ -H "Content-Type: application/json" \ -d '{"mode":"error","rate":1.0}'
./swiftdeploy promote stable
./swiftdeploy promote canary
curl -sS -X POST "http://127.0.0.1:8080/chaos" \ -H "Content-Type: application/json" \ -d '{"mode":"error","rate":1.0}'
./swiftdeploy promote stable
./swiftdeploy promote canary
curl -sS -X POST "http://127.0.0.1:8080/chaos" \ -H "Content-Type: application/json" \ -d '{"mode":"error","rate":1.0}'
./swiftdeploy promote stable
curl -sS -X POST "http://127.0.0.1:8080/chaos" \ -H "Content-Type: application/json" \ -d '{"mode":"recover"}'
./swiftdeploy promote stable
curl -sS -X POST "http://127.0.0.1:8080/chaos" \ -H "Content-Type: application/json" \ -d '{"mode":"recover"}'
./swiftdeploy promote stable
curl -sS -X POST "http://127.0.0.1:8080/chaos" \ -H "Content-Type: application/json" \ -d '{"mode":"recover"}'
./swiftdeploy promote stable - Clients hit Nginx.
- Nginx proxies to the Go app on the internal network.
- The swiftdeploy CLI generates configs and asks OPA for policy decisions on loopback (127.0.0.1), not through public ingress. - template-output/nginx.conf
- template-output/docker-compose.yml - validate confirms preflight health,
- deploy starts policy sidecar + stack,
- promote switches stable/canary mode safely,
- status and audit leave an evidence trail. - Pre-deploy gate
Host conditions (disk/cpu/memory snapshot) are checked against policy before full startup proceeds.
- Pre-promote gate (canary -> stable)
Windowed metrics from /metrics are evaluated before promotion completes. - Policy paths are easy to get subtly wrong.
POST /v1/data/policy/infrastructure/decision is valid.
POST /v1/data/infrastructure/decision often returns {} and can waste debugging time.
- Ingress isolation should be proven, not assumed.
Hitting OPA-shaped URLs on Nginx should return app-level 404/non-OPA response, while loopback OPA returns policy JSON.
- Canary tests need traffic.
Windowed error-rate checks can look “healthy” if there is no meaningful request volume during the window.
- Generated files are outputs, not source files.
If you edit generated Compose/Nginx files directly, init will overwrite them. - declarative (manifest.yaml as source of truth),
- reproducible (templates + generated configs),
- observable (/metrics, status),
- enforceable (OPA gates),
- auditable (history.jsonl -> audit_report.md). - validate reports all checks PASS.
- deploy ends with stack healthy.
- promote canary enables X-Mode: canary on /healthz.
- promote stable removes X-Mode after successful gate.
- status appends records to history.jsonl.
- audit generates audit_report.md. - Metrics families visible.
- OPA responses contain a result object and allow. - 404 or non-OPA response via Nginx path.