Skip to content

Arquitectura QA · digital-jungle

This content is not available in your language yet.

WaveDescripciónEstadoCommit
Wave 0Pre-requisitos humanos (dominio, cap, SES)✅ DONE
Wave 1Foundations — IAM, ECR, VPC, SG, KMS, SSM, Route53, ACM✅ LIVE905a8c1
Wave 2Datastores — EFS + S3✅ LIVE905a8c1
Wave 2bRDS Postgres 16.14 CQRS (primary + replica db.t4g.micro)🔄 Provisionando20f2267
Wave 3Routing — ALB + target groups + R53 alias⏳ Pendiente
Wave 4ECS services (10 tasks)⏳ Pendiente
Wave 5Frontends — admin-console, tenant-portal, wallet⏳ Pendiente
Wave 6Observabilidad — CloudWatch alarms + dashboard⏳ Pendiente
Wave 7Deploy humano — build, migrate, force-new, smoke⏳ Pendiente

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)
// Patrón establecido en cada módulo backend
type 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.

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 pending
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" : 1
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)
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 live
CommitFechaDescripción
905a8c12026-05-31Wave 1 (Foundations) — 62 recursos: IAM, ECR, SG, KMS, SSM, Route53, ACM, EFS, S3
11c55872026-05-31Fase 1 blockchain anchoring — VC anchor batch + KMS EIP-1559 + Polygon Amoy
dea97802026-05-31RDS CQRS code — dual pgxpool writer/reader en todos los módulos Go
20f22672026-05-31RDS fix: Postgres 16.14 + sin shared_preload_libraries = vector (pgvector disponible sin preload en PG 16)