Arquitectura QA · digital-jungle
Estado de Waves
Sección titulada «Estado de Waves»| Wave | Descripción | Estado | Commit |
|---|---|---|---|
| Wave 0 | Pre-requisitos humanos (dominio, cap, SES) | ✅ DONE | — |
| Wave 1 | Foundations — IAM, ECR, VPC, SG, KMS, SSM, Route53, ACM | ✅ LIVE | 905a8c1 |
| Wave 2 | Datastores — EFS + S3 | ✅ LIVE | 905a8c1 |
| Wave 2b | RDS Postgres 16.14 CQRS (primary + replica db.t4g.micro) | 🔄 Provisionando | 20f2267 |
| Wave 3 | Routing — ALB + target groups + R53 alias | ⏳ Pendiente | — |
| Wave 4 | ECS services (10 tasks) | ⏳ Pendiente | — |
| Wave 5 | Frontends — admin-console, tenant-portal, wallet | ⏳ Pendiente | — |
| Wave 6 | Observabilidad — CloudWatch alarms + dashboard | ⏳ Pendiente | — |
| Wave 7 | Deploy humano — build, migrate, force-new, smoke | ⏳ Pendiente | — |
Decisión: Aurora descartado
Sección titulada «Decisión: Aurora descartado»Aurora Serverless v2 fue evaluado en Wave 0 y descartado por costo:
- Aurora mínimo: ~$48/mes por instancia (vs RDS db.t4g.micro ~$12.60/mes)
- Con los volúmenes actuales de QA, RDS single-tenant con CQRS manual cubre el requisito
- Decision log:
dea9780(RDS CQRS code) — dual pgxpool en Go (writer endpoint + reader endpoint)
CQRS en Go — dual pgxpool
Sección titulada «CQRS en Go — dual pgxpool»// Patrón establecido en cada módulo backendtype DB struct { Writer *pgxpool.Pool // primary RDS endpoint (escrituras + transacciones) Reader *pgxpool.Pool // replica RDS endpoint (lecturas analíticas / reports)}Las queries de escritura van siempre por Writer (que honra el app.current_tenant de RLS).
Las queries de lectura intensiva pueden ir por Reader para descargar el primary.
1 · Topología de red
Sección titulada «1 · Topología de red»VPC us-east-1 · 3 layers de routing: Internet → ALB → ECS Service Connect.
graph TB subgraph internet["🌐 Internet"] users[Usuarios · operadores · equipo bjungle] end
subgraph dns["Route53 + CloudFront · *.qa.bjungle.net"] r53[Route53 hosted zone<br/>bjungle.net / qa.bjungle.net] cf1[CloudFront admin-console-qa] cf2[CloudFront portal-qa] cf3[CloudFront wallet-qa] acm[ACM cert *.qa.bjungle.net] end
subgraph s3buckets["S3 frontends (privados, OAC)"] s3a[admin-console-qa] s3b[tenant-portal-qa] s3c[user-wallet-qa] end
subgraph vpc["VPC · us-east-1 · account 767397981954"] subgraph publicLayer["Subnets públicas DMZ · NAT GW"] alb[ALB bjungle-qa-alb<br/>4 host-based rules] nat[NAT Gateway] end
subgraph appSubnets["Subnets app privadas · 1a + 1b multi-AZ"] subgraph ecsCluster["ECS Cluster bjungle-qa · Fargate-only"] platformApi[platform-api 0.25/0.5GB] platformWorker[platform-worker 0.25/0.5GB] bmonkeyApi[bmonkey-api 0.5/1.0GB] bmonkeyWorker[bmonkey-worker 0.25/0.5GB] bhawkApi[bhawk-api 0.25/0.5GB] bhawkWorker[bhawk-worker 0.25/0.5GB] bsealApi[bseal-api 0.25/0.5GB] bsealWorker[bseal-worker 0.5/1.0GB] arcfaceTask[arcface 1.0/2.0GB] natsTask[nats 0.25/0.5GB · JetStream] end end
subgraph dbSubnets["Subnets DB privadas · 1a + 1b"] rds[(RDS Postgres 16.14<br/>db.t4g.micro · CQRS<br/>primary + replica<br/>4 DBs lógicas + pgvector<br/>⚠️ sin shared_preload_libraries vector)] end
subgraph efsLayer["EFS regional bursting · Wave 2 LIVE"] efsNats[/nats-jetstream] efsArc[/arcface-models] end end
subgraph awsExternal["AWS Managed Services us-east-1"] bedrock[Bedrock Claude Haiku 4.5] rek[Rekognition Collection + Liveness] kms1[KMS bmonkey-anchor ECC_SECG_P256K1] kms2[KMS bseal RSA_2048] kms3[KMS bmonkey-issuer ECC_NIST_P256] ses[SES noreply@qa.bjungle.net] ssm[SSM /bjungle/qa/* SecureString] cw[CloudWatch Logs 14d + Container Insights] end
subgraph external["Externos"] polygon[Polygon Amoy RPC Alchemy] mp[Mercado Pago sandbox] end
users --> r53 r53 --> cf1 & cf2 & cf3 r53 --> alb acm -.TLS.-> cf1 & cf2 & cf3 & alb
cf1 -.OAC.-> s3a cf2 -.OAC.-> s3b cf3 -.OAC.-> s3c
alb --> platformApi & bmonkeyApi & bhawkApi & bsealApi
bmonkeyWorker -.egress.-> polygon platformApi -.egress.-> mp
classDef live fill:#10b981,stroke:#047857,color:#fff classDef pending fill:#6b7280,stroke:#374151,color:#fff classDef data fill:#3b82f6,stroke:#1e40af,color:#fff
class efsNats,efsArc live class rds data class platformApi,bmonkeyApi,bhawkApi,bsealApi pending2 · Mapa de costos (real vs proyectado)
Sección titulada «2 · Mapa de costos (real vs proyectado)»pie title Costo mensual real ~ USD 120/mes (proyectado USD 121) "ECS Fargate compute" : 59 "ALB + LCU" : 20 "RDS Postgres (primary + replica db.t4g.micro)" : 25 "CloudWatch + Container Insights" : 6 "KMS keys (3 keys)" : 3 "Frontends S3 + CloudFront" : 4 "NAT GW + Route53 + ACM" : 1 "Bedrock + Rekognition" : 1 "EFS + S3 docs + SSM" : 13 · Data flow · onboarding end-to-end
Sección titulada «3 · Data flow · onboarding end-to-end»sequenceDiagram actor user as Usuario participant cf as CloudFront wallet-qa.bjungle.net participant alb as ALB api-qa.bjungle.net participant bm as bmonkey-api participant bh as bhawk-api participant pgsql as RDS Postgres 16.14 participant arc as arcface participant nats as NATS JetStream participant bmw as bmonkey-worker participant kms as KMS anchor participant chain as Polygon Amoy
user->>cf: GET wallet-qa.bjungle.net cf-->>user: SPA (S3 + OAC)
user->>alb: POST /v1/embed/sessions/{token} alb->>bm: route bmonkey-api:8081 bm->>pgsql: SELECT flow + create session (Writer pool) bm-->>user: 200 session_id
user->>alb: POST /advance step=face_match bm->>arc: HTTP /embed selfie b64 arc-->>bm: 512-dim vector bm->>pgsql: pgvector ANN search (Reader pool) bm-->>user: 200 advance OK
user->>alb: POST /advance step=sarlaft bm->>bh: POST /v1/internal/screenings X-Internal-Token bh->>pgsql: SELECT rules + evaluación (Writer pool) bh-->>bm: {decision: "approve"} bm->>pgsql: INSERT VC + wallet_grants + outbox (Writer pool) bm-->>user: 200 advance OK
Note over bmw,chain: Anchor batch cron cada 4h (EventBridge)
bmw->>pgsql: SELECT vc_anchor_candidates (Reader pool) bmw->>kms: Sign EIP-1559 tx ECDSA_SHA_256 kms-->>bmw: signature bmw->>chain: SubmitTransaction calldata=merkle_root chain-->>bmw: tx_hash bmw->>pgsql: INSERT vc_anchor_batches (Writer pool)4 · Capas de seguridad
Sección titulada «4 · Capas de seguridad»graph TB subgraph layer1["Layer 1 · Edge"] cf_oac[OAC · S3 buckets privados] tls[TLS 1.2+ · ACM *.qa.bjungle.net] end
subgraph layer2["Layer 2 · ALB"] alb_sg[SG-ALB · ingress 443 only] host_routing[Host-based routing · 4 reglas] end
subgraph layer3["Layer 3 · ECS network"] tasks_sg[SG-tasks · solo desde SG-ALB] privnet[Subnets app privadas · sin IP pública] sc_dns[Service Connect · bjungle-qa.local] end
subgraph layer4["Layer 4 · IAM least-privilege"] task_role[Task role por servicio · ARNs específicos] exec_role[Execution role · ECR + SSM /bjungle/qa/<app>/*] end
subgraph layer5["Layer 5 · Data plane"] rds_sg[SG-RDS · 5432 solo desde SG-tasks] pg_rls[Row-Level Security · app.current_tenant] secdef[SECURITY DEFINER · globals bmonkey] cqrs[CQRS dual-pool · Writer / Reader] end
subgraph layer6["Layer 6 · Crypto"] kms_anchor[KMS ECC_SECG_P256K1 · anchor] kms_bseal[KMS RSA_2048 · bseal sign] kms_issuer[KMS ECC_NIST_P256 · VC JWT] ssm_enc[SSM SecureString · aws/ssm KMS] end
layer1 --> layer2 --> layer3 --> layer4 --> layer5 layer4 --> layer6
classDef live fill:#10b981,stroke:#047857,color:#fff class cf_oac,tls,alb_sg,host_routing,tasks_sg,privnet,sc_dns,task_role,exec_role live5 · Implementation log
Sección titulada «5 · Implementation log»| Commit | Fecha | Descripción |
|---|---|---|
905a8c1 | 2026-05-31 | Wave 1 (Foundations) — 62 recursos: IAM, ECR, SG, KMS, SSM, Route53, ACM, EFS, S3 |
11c5587 | 2026-05-31 | Fase 1 blockchain anchoring — VC anchor batch + KMS EIP-1559 + Polygon Amoy |
dea9780 | 2026-05-31 | RDS CQRS code — dual pgxpool writer/reader en todos los módulos Go |
20f2267 | 2026-05-31 | RDS fix: Postgres 16.14 + sin shared_preload_libraries = vector (pgvector disponible sin preload en PG 16) |
Referencias
Sección titulada «Referencias»- ADR-008 blockchain strategy — KMS anchor key + Polygon Amoy
- Proceso de agentes — handshake tech-lead + Well-Architected
- Wallet de identidad — SSI + VC anchoring en QA