Files
dd0c/products/03-alert-intelligence/architecture/dual-mode-addendum.md
Max Mayfield c3bafa238a Add dual-mode deployment addendums for all 6 products
P1 route: 16 pts (template, full docker-compose + install script)
P2 drift: 17 pts (pgmq, local CA for mTLS)
P3 alert: 19 pts (Lambda→Fastify, DynamoDB→PG JSONB)
P4 portal: 18 pts (Step Functions→cron, Aurora→PG+pgvector)
P5 cost: 19 pts (EventBridge→agent/polling, DynamoDB→PG JSONB)
P6 run: 15 pts (easiest — already PG-native, no AWS deps in core)

Total self-hosted effort: ~104 story points across all 6 products
2026-03-01 02:00:00 +00:00

2.8 KiB

dd0c/alert — Dual-Mode Deployment Addendum

Template: Based on dd0c/route dual-mode pattern


Cloud → Self-Hosted Service Mapping

Cloud Service Self-Hosted Replacement Notes
API Gateway + Lambda Fastify/Express container Webhook ingestion endpoint
SQS PostgreSQL pgmq Between ingestion and correlation
ECS Fargate (Correlation) Docker container Same Node.js code
DynamoDB PostgreSQL (JSONB) Incidents stored as JSONB with GIN indexes
TimescaleDB on RDS TimescaleDB container Analytics unchanged
Cognito Local JWT (HS256) AuthProvider pattern
S3 (raw payload archive) Local FS or MinIO ObjectStore trait
SES SMTP relay Email notifications
EventBridge Cron container Scheduled tasks

Self-Hosted Compose Services

services:
  ingestion:        # Webhook endpoint (replaces API Gateway + Lambda)
    image: ghcr.io/dd0c/alert-ingestion:latest
  correlation:      # Correlation engine (replaces ECS Fargate)
    image: ghcr.io/dd0c/alert-correlation:latest
  api:              # Dashboard API
    image: ghcr.io/dd0c/alert-api:latest
  dashboard:        # React SPA
    image: ghcr.io/dd0c/alert-dashboard:latest
  postgres:         # Incidents (JSONB) + config
    image: postgres:16-alpine
  timescaledb:      # Analytics
    image: timescale/timescaledb:latest-pg16
  redis:            # Sliding windows for correlation, circuit breakers
    image: redis:7-alpine
  caddy:            # Reverse proxy + auto-TLS
    image: caddy:2-alpine

Key Difference: DynamoDB → PostgreSQL JSONB

DynamoDB Single-Table design maps to PostgreSQL JSONB with partition-like indexes:

CREATE TABLE incidents (
  id UUID PRIMARY KEY,
  tenant_id TEXT NOT NULL,
  data JSONB NOT NULL,
  severity TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'open',
  created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_incidents_tenant ON incidents(tenant_id);
CREATE INDEX idx_incidents_data ON incidents USING GIN(data);
-- RLS policy
ALTER TABLE incidents ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON incidents USING (tenant_id = current_setting('app.tenant_id'));

Epic Impact

Epic Change Effort
1 (Webhook Ingestion) Lambda → Fastify container 3 pts
2 (Normalization) No change — pure logic 0
3 (Correlation) pgmq instead of SQS, same Redis 2 pts
4 (Notifications) SMTP fallback 1 pt
5 (Slack Bot) No change 0
6 (Dashboard API) LocalAuthProvider, DynamoDB→PG 3 pts
7 (Dashboard UI) Local login form 2 pts
8 (Infrastructure) docker-compose.yml + install.sh 5 pts
9 (Onboarding) Local signup, remove Stripe req 3 pts
10 (TF Tenets) No change 0
Total 19 pts