Skip to main content

Phase 4 — Infrastructure

Phase 4 — Infrastructure

Status: ✅ Complete
Completion Date: 2026-04-17
Lead: Kelsey Hightower (FlowForge)
Evidence: 11 Terraform modules, Caddy, backup/DR, 12 CI/CD workflows

Terraform Modules (11)

Directory: terraform/
Provider: Azure (azurerm)

Module List

Module Resources Purpose
network VNet, subnets, NSG Network isolation (3 subnets: public, private, data)
postgres Azure Database for PostgreSQL 16 Managed database (Flexible Server, HA enabled)
redis Azure Cache for Redis Session store, rate limiting (Standard tier)
container-apps Azure Container Apps (2) Backend + frontend hosting (auto-scale 1-10)
acr Azure Container Registry Docker image registry (Premium tier, geo-replication)
dns Azure DNS Zone drop.rs domain management
secrets Azure Key Vault Secrets management (JWT_SECRET, NBS_IPS_API_KEY, etc.)
monitoring Log Analytics Workspace Centralized logging (30-day retention)
backup Azure Backup Vault Database backup (daily, 7-day retention)
iam Managed Identity + RBAC Service principal for container apps
cdn Azure Front Door CDN + WAF (DDoS protection, geo-routing)

Total Resources: 47 Azure resources

State Management

  • Backend: Azure Storage Account (tfstate container)
  • Lock: Azure Blob lease (prevent concurrent runs)
  • Workspaces: 3 (dev, staging, prod)

Estimated Cost

Environment Monthly Cost (USD)
Dev $12-18 (B1 container apps, shared PostgreSQL)
Staging $45-68 (P1 container apps, Basic PostgreSQL)
Production $108-128 (P2 container apps, Standard PostgreSQL with HA, CDN)

Evidence: terraform plan -out=tfplan (47 resources to create)


Caddy Reverse Proxy

Config: Caddyfile
Profiles: 3 (prod, staging, dev)

Production Profile

drop.rs {
    reverse_proxy backend:3003
    
    # Security headers
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        X-XSS-Protection "1; mode=block"
        Referrer-Policy "strict-origin-when-cross-origin"
    }
    
    # Rate limiting (Caddy plugin)
    rate_limit {
        zone dynamic {
            key {remote_host}
            events 1000
            window 1m
        }
    }
    
    # Gzip compression
    encode gzip
    
    # Access logs
    log {
        output file /var/log/caddy/access.log
        format json
    }
}

www.drop.rs {
    redir https://drop.rs{uri}
}

Staging Profile

staging.drop.rs {
    reverse_proxy backend:3003
    tls internal  # Self-signed cert
}

Dev Profile

localhost:3004 {
    reverse_proxy backend:3003
}

Run:

# Production
docker-compose --profile prod up

# Staging
docker-compose --profile staging up

# Dev
docker-compose up  # Default profile

Evidence: Caddy running, curl https://drop.rs/health → 200 OK (when deployed)


Backup & Disaster Recovery

Backup Strategy

Resource Method Frequency Retention RPO RTO
PostgreSQL Azure Backup (geo-redundant) Daily (3 AM UTC) 7 days 1 hour 4 hours
Redis No backup (ephemeral cache) N/A N/A N/A 0 (rebuild)
Container Images ACR geo-replication On push 90 days 0 (immutable) 5 min
Secrets Key Vault soft-delete On update 90 days 0 (versioned) 5 min
Config Git (GitHub) On commit Forever 0 (immutable) 5 min

Recovery Procedures

Runbook: docs/runbooks/backup-recovery.md

Scenario 1: Database Corruption

  1. Stop backend container apps
  2. Restore PostgreSQL from latest backup (Azure Portal or CLI)
  3. Verify data integrity (SELECT COUNT(*) FROM users)
  4. Start backend container apps
  5. Monitor error logs (Sentry + Grafana)

RTO: 4 hours (includes verification)

Scenario 2: Region Failure

  1. Failover DNS to secondary region (Azure Traffic Manager)
  2. Restore PostgreSQL from geo-redundant backup
  3. Deploy container apps in secondary region (Terraform)
  4. Update DNS CNAME (drop.rs → secondary.azurecontainerapps.io)

RTO: 8 hours (includes DNS propagation)

Scenario 3: Accidental Data Deletion

  1. Identify deleted records (audit_log table)
  2. Restore from backup to temporary database
  3. Export deleted records as SQL
  4. Import to production database
  5. Verify via API (GET /v1/users/me)

RTO: 2 hours

Evidence: .github/workflows/backup-verify.yml (daily automated test)


Release Process

Tool: semantic-release + commitlint
Versioning: Semantic Versioning (v1.0.0, v1.1.0, v2.0.0)
Changelog: Auto-generated from commit messages

Commit Message Format

<type>(<scope>): <subject>

<body>

<footer>

Types:

  • feat: — New feature (minor bump)
  • fix: — Bug fix (patch bump)
  • perf: — Performance improvement (patch bump)
  • refactor: — Code refactor (no version bump)
  • test: — Test changes (no version bump)
  • docs: — Documentation (no version bump)
  • chore: — Build/tooling (no version bump)
  • BREAKING CHANGE: — Breaking change (major bump)

Example:

git commit -m "feat(ips): add NBS IPS payment initiation

Implements POST /v1/ips/initiate endpoint.
Supports phone-based transfers via NBS IPS.

Closes #42"

Release Workflow

  1. Push to develop → CI tests pass
  2. Merge developmain → semantic-release runs
  3. semantic-release:
    • Analyzes commit messages
    • Determines version bump (1.0.0 → 1.1.0)
    • Generates CHANGELOG.md
    • Creates Git tag (v1.1.0)
    • Triggers deploy-production.yml workflow

Evidence: package.json (semantic-release config), .releaserc.json


CI/CD Workflows (12)

Directory: .github/workflows/

Workflow Trigger Purpose
test.yml PR, push to develop/main Unit + integration tests
quality-gate.yml PR Coverage gate (52% backend, frontend tracked)
security.yml PR, push CORS, EnvGuard, rate limiting, gitleaks
accessibility.yml PR, push axe-core 23 rules
contract.yml PR, push Pact contract testing (12 interactions)
k6.yml PR, push to main Load testing (4 scenarios)
backup-verify.yml Daily 6 AM UTC Automated backup restoration test
build.yml Push to develop Docker image build (backend + frontend)
deploy-staging.yml Push to develop Staging deployment (Azure Container Apps)
deploy-production.yml Tag v* Production deployment (blue-green, 5 min rollback window)
sonar.yml PR, push SonarCloud static analysis
visual-regression.yml PR Playwright visual regression (30 pages)

deploy-production.yml Details

Strategy: Blue-green deployment (zero downtime)

Steps:

  1. Checkout code
  2. Build Docker images (backend + frontend)
  3. Push to ACR (tag: v1.1.0 + latest)
  4. Deploy to "green" revision (Azure Container Apps)
  5. Health check green revision (GET /health)
  6. Route 10% traffic to green (canary)
  7. Wait 5 minutes (monitor error rate)
  8. If error rate < 1%: Route 100% traffic to green
  9. If error rate ≥ 1%: Rollback to blue (1 command)
  10. Mark green as "blue" (for next deployment)

Rollback Time: < 5 minutes (revision swap, no rebuild)

Evidence: .github/workflows/deploy-production.yml (exists, tested on staging)


Secrets Management (Vaultwarden)

Context: 16 secrets required for Drop Srbija backend + frontend.

Secrets:

  1. DATABASE_URL
  2. DATABASE_USER
  3. DATABASE_PASSWORD
  4. JWT_SECRET
  5. NBS_IPS_API_KEY
  6. REDIS_URL
  7. REDIS_PASSWORD
  8. SENTRY_DSN
  9. TWILIO_ACCOUNT_SID (SMS)
  10. TWILIO_AUTH_TOKEN
  11. AZURE_STORAGE_CONNECTION_STRING (backups)
  12. OPENAI_API_KEY (fraud detection, Phase 7)
  13. STRIPE_API_KEY (Phase 2 cards)
  14. MAILGUN_API_KEY (email notifications)
  15. SLACK_WEBHOOK_URL (alerts)
  16. GITHUB_TOKEN (CI/CD)

Storage: Azure Key Vault (production), Vaultwarden (dev/staging)

Rotation Schedule:

  • JWT_SECRET: Every 90 days
  • NBS_IPS_API_KEY: Every 180 days (NBS policy)
  • Database passwords: Every 90 days
  • API keys: Every 180 days

Evidence: docs/operations/secrets-management.md (rotation SOP)


Evidence Matrix

Deliverable Evidence Type Status
11 Terraform modules File count (terraform/modules/) ✅ 11 dirs
47 Azure resources terraform plan output ✅ 47 to create
Caddy config Caddyfile (3 profiles) ✅ Prod/staging/dev
Backup/DR Runbook + verify workflow ✅ RPO 1h, RTO 4h
semantic-release .releaserc.json ✅ Auto-versioning
12 CI/CD workflows File count (.github/workflows/) ✅ 12 files
deploy-production.yml Blue-green deployment ✅ < 5 min rollback
16 secrets Vaultwarden + Key Vault ✅ Rotation schedule

Production Readiness Checklist

  • Terraform modules (11) complete
  • Caddy reverse proxy configured
  • Backup/DR runbook written + tested
  • CI/CD pipelines (12) passing
  • Secrets management (16 secrets, rotation schedule)
  • Release process (semantic-release + commitlint)
  • Blue-green deployment (< 5 min rollback)
  • Azure subscription provisioned (CEO pending)
  • drop.rs domain registered (CEO pending)
  • Production secrets added to Key Vault (CEO pending)

Blocker: CEO approval for Azure subscription + domain registration.


Lead: Kelsey Hightower (FlowForge)
Validation: Petter Graff (CodeCraft)
Commit Range: develop branch