# Monetarie — Roteiro de Build AWS (design canônico)

> Data: 2026-06-20 · Autor: time Vulci · Status: **desenho validado empiricamente, pronto para execução faseada**
> Fonte de verdade: validação ao vivo da AWS (8-agent workflow) + leitura do monorepo Monetarie de referência (`/Users/luizpenha/monetarie`).
> Regra de ouro: **zero tentativa-e-erro**. Cada camada é validada antes da próxima. Nada é aplicado na AWS sem OK explícito do dono.

---

## 0. Instituição & contas

| Campo | Monetarie | Monetarie (molde) |
|---|---|---|
| Razão social | MONETARIE SOCIEDADE DE CRÉDITO DIRETO S.A. | SICOOB Monetarie (cooperativa) |
| Tipo | **SCD** (Sociedade de Crédito Direto) | Cooperativa de Crédito |
| CNPJ | 46.026.562/0001-05 | 00.512.839/0001-09 |
| ISPB | 46026562 | 00512839 |
| COMPE | **526** | 098 |
| SISBACEN | **00018** | 54106 |
| Domínio público | monetarie.com.br | monetarie.com.br |
| Zona privada | monetarie.internal | monetarie.internal |
| Conta AWS | **990933657879** (user `vulciaws`) | 364807861246 |
| Credenciais CLI | Conta Monetarie isolada, `sa-east-1` | monetarie |

**Permissão validada:** `vulciaws` = `AdministratorAccess` (+ SystemAdmin/DatabaseAdmin/NetworkAdmin). Sem bloqueio.

---

## 1. Topologia de rede (validada na AWS)

### 1.1 Estado pré-existente na conta 990933657879
- VPC `vpc-0d5ecd0df0976bcb9` = **192.168.0.0/16** (`monetarie-rtm`, IBM-MQ + CloudHSM-HML + bastion `i-06be52454b879cbbb`), subnets prod/hml/dr.
- VPC default `vpc-070f1ff919057a761` = 172.31.0.0/16.
- **TGW `tgw-051f1a3c159dfb25b`** (RT `tgw-rtb-00c87955e8b181f3f`), ASN 64513.
- **Direct Connect HOSTED, vivo, BGP up**: `dxcon-fh7fdtyv` (RTMRJ, EQRJ2, VLAN 343) + `dxcon-fgl2a4wd` (RTMSP, TNDB, VLAN 347), 50 Mbps cada, **donos = nós**. Cada conexão hosted = **1 VIF**, e esse VIF é **transit, da Lerian** (`975049942889`) → DXGW `976ff838-…` (da Lerian) → TGW.
- VGW próprio disponível: `vgw-0f3f2fb334d1e509d` (ASN 64512), preso à 192.168/16, sem VPN ativa.
- Prefixos RSFN já ativos na RT do TGW (propagados do DXGW): `200.218.64.0/18`, `/19`s, `200.160.161.0/24`, `200.160.163.0/24`, `172.31.1-3.0/24`, `10.173.1.247/32`. `192.168.0.0/16` propagado da nossa VPC.
- App layer **vazio** (sem ECS/ALB/RDS/ElastiCache/ECR/WAF/Secrets). Nenhuma zona `monetarie.internal`/`.com.br`.

### 1.2 Decisão de RSFN: usar a rota existente + tradução por NAT no range `192.168/16`
**Restrição física:** o DXGW só anuncia `192.168.0.0/16` ao RTM (allowed-prefix). Como **não vamos pedir nada à Lerian nem ao RTM**, o tráfego do app precisa **se apresentar como `192.168.x.x`** (range já aceito). Solução = **egress centralizado com SNAT**.

```
 App VPC 10.40.0.0/16
   ├─ DNS: pod resolve *.rsfn.net.br
   │   → Route53 Resolver OUTBOUND + regra FORWARD "rsfn.net.br."
   │   → BIND9 forwarder @192.168.x.x → DNS do RTM (sobre o DX)
   └─ Tráfego p/ prefixos RSFN (200.218.x, 200.160.16x, 192.168.69/70, MQ 192.168.70.72…)
       → TGW (RT dedicada do attachment do app)  → VPC 192.168/16
       → appliance SNAT/egress @192.168.x.x (iptables MASQUERADE, appliance-mode no TGW attach)
           source vira 192.168.x.x  ← TRADUÇÃO p/ o range aceito
       → RT da 192.168/16 (RSFN → TGW) → DXGW → DX → RTM/RSFN
           (retorno chega no 192.168.x.x → SNAT desfaz → TGW → app VPC)
```

**Princípios:**
- **Nada novo solicitado** ao RTM/Lerian; nenhum VIF privado; reusa o circuito que já é nosso.
- **RT do TGW da Lerian (`tgw-rtb-00c8…`) NÃO é alterada** — criamos uma **RT de TGW dedicada** para o attachment do app, isolando blast-radius.
- Espelha o padrão Monetarie (Route53 Resolver + BIND9 + appliance de egress BACEN).
- Recursos novos mínimos: **1 instância arm64 `t4g.micro`** (SNAT + BIND9), na 192.168/16 (único lugar onde o IP-fonte pertence ao range aceito) numa subnet dedicada nossa (`192.168.40.0/24`, aditiva).

**Nuance honesta:** os pacotes ainda transitam fisicamente pelo DXGW da Lerian (é a única rota RSFN existente). O que eliminamos é **qualquer dependência de pedido** a Lerian/RTM. Corte físico total = caminho de VIF privado próprio (documentado em §1.4), não acionado agora.

### 1.3 Validação de conectividade RSFN (PoC obrigatório antes de "produção")
Esta é a única peça que exige **prova empírica de campo** (sem terceiros — testamos do nosso appliance):
1. Subir o appliance SNAT/BIND9 na 192.168/16.
2. Do appliance: `dig @<dns-rtm> dict-h.pi.rsfn.net.br` e `nc -vz <ip-rsfn-bacen> <porta>` sobre o DX → confirmar resposta.
3. De um pod no 10.40/16: via TGW→appliance→DX, repetir e confirmar SNAT (tcpdump no appliance mostra source 192.168.x.x).
4. Só então marcar RSFN como "GO". **Itens a descobrir (do ambiente 192.168/16 existente, sem terceiros):** IP do DNS do RTM; lista exata de prefixos/portas por cabine.

### 1.4 Alternativa de corte físico (NÃO acionar agora — registrada)
RTM/Equinix realocar o VIF hosted como **VIF privado → VGW `vgw-0f3f...` → app VPC** (remove a Lerian do caminho físico). Requer ticket RTM. Fora de escopo por decisão do dono.

### 1.5 CIDR & subnets do app (VPC nova `10.40.0.0/16` — validado livre)
| Tier | sa-east-1a | sa-east-1b | sa-east-1c |
|---|---|---|---|
| public | 10.40.0.0/20 | 10.40.16.0/20 | 10.40.32.0/20 |
| private (app/ECS) | 10.40.64.0/20 | 10.40.80.0/20 | 10.40.96.0/20 |
| data (Aurora/Redis) | 10.40.128.0/20 | 10.40.144.0/20 | 10.40.160.0/20 |

NAT GW único (custo). IGW para os ALBs. TGW attachment para o caminho RSFN.

---

## 2. Sequência de Terraform (por camadas, `infra/aws/`)

State remoto: bucket **`monetarie-tfstate-990933657879`**, key `homolog/greenfield/terraform.tfstate`, lock DynamoDB **`monetarie-tf-lock`**, usando credenciais da conta Monetarie.

| # | Camada | Arquivos | Depende de |
|---|---|---|---|
| 0 | **bootstrap** | `infra/aws/bootstrap/` (bucket tfstate + lock) | — |
| 1 | **network** | `vpc.tf`, `security-groups.tf`, `tgw-attach.tf`, `rsfn-egress.tf` | 0 |
| 2 | **dns** | `dns.tf` (zona privada `monetarie.internal` + Resolver) | 1 |
| 3 | **data** | `aurora.tf` (1 cluster 0,5–4 ACU), `elasticache.tf` (1× t4g.micro), `kms-secrets.tf` | 1 |
| 4 | **registry** | `ecr.tf` (15 repos `monetarie/*`) | 0 |
| 5 | **compute** | `ecs-cluster.tf`, `ecs-capacity-ec2-arm64.tf`, `ecs-services.tf` | 1,3,4 |
| 6 | **edge** | `alb.tf` (público + **WAFv2**), `alb-internal.tf` (HTTP-only) | 1,2,5 |
| 7 | **messaging** | NATS 3×EC2, TigerBeetle 1×EC2 (self-managed) | 1 |

Convenção de nomes: `monetarie-*` → `monetarie-*`, KMS `alias/monetarie-{core,pix,spb,npc,sta,clst}`, secrets `monetarie/{env}/{domain}/{name}`, DBs `mon_{core,pix,spb,npc,sta,clst}`.

---

## 3. Dados (Aurora) — confirmado pelo dono

- **1 cluster único** `monetarie-core-<env>` (Aurora PostgreSQL Serverless v2, engine 16.x).
- **`MinACU = 0.5` + autoscaling nativo**, **`MaxACU = 4`** (teto de custo; Monetarie usa 0,5–8 ao vivo).
- 1 writer `db.serverless`, MultiAZ off (homolog), `StorageEncrypted` (KMS `monetarie-core`), backup 7d.
- 6 bancos lógicos: `mon_core, mon_pix, mon_spb, mon_npc, mon_sta, mon_clst`.
- SG do DB libera 5432 **somente do SG do ECS** (sem CIDR). Subnet group = tier `data`.
- Redis: 1 nó `cache.t4g.micro` redis 7.x (sem replication group / sem TLS — `redis://`).

---

## 4. Catálogo de serviços (15 ECS) — `monetarie-*` → `monetarie-*`

Cluster `monetarie-greenfield-<env>`. Capacity: FARGATE (default base 1) + FARGATE_SPOT + EC2 arm64 `monetarie-gf-ec2-arm64-<env>` (1× t4g.medium, só para `core-api` por causa do io_uring do TigerBeetle).

| Serviço | Porta | CPU/MEM | Launch | ECR | Depende de |
|---|---|---|---|---|---|
| core-api | 4000 | 512/1024 | **EC2 arm64** | monetarie/core-api | Aurora mon_core, Redis, NATS, TigerBeetle |
| pix-api | 4002 | 512/1024 | Fargate | monetarie/pix-api | mon_pix, Redis, NATS, BACEN mTLS |
| spb-api (+mq-sidecar 9080) | 4010 | 1024/2048 | Fargate | monetarie/spb-api, /spb-mq-sidecar | mon_spb, NATS, IBM MQ RTM, mTLS |
| npc-api | 4020 | 256/512 | Fargate | monetarie/npc-api | mon_npc, NATS |
| sta-api | 4000 | 256/512 | Fargate | monetarie/sta-api | mon_sta, NATS, S3 CADOC |
| clst-api | 4040 | 256/512 | Fargate | monetarie/clst-api | mon_clst, NATS |
| core-admin-ui | 8080 | 256/512 | Fargate | monetarie/core-admin-ui | core-api (interno) |
| core-banking-ui | 8080 | 256/512 | Fargate | monetarie/core-banking-ui | core-api |
| core-merchant-ui | 8080 | 256/512 | Fargate | monetarie/core-merchant-ui | core-api |
| pix-admin-ui | 8080 | 256/512 | Fargate | monetarie/pix-admin-ui | pix-api |
| spb-admin-ui | 8080 | 256/512 | Fargate | monetarie/spb-admin-ui | spb-api |
| npc-admin-ui | 8080 | 256/512 | Fargate | monetarie/npc-admin-ui | npc-api |
| sta-admin-ui | 8080 | 256/512 | Fargate | monetarie/sta-admin-ui | sta-api |
| backoffice | 8080 | 256/512 | Fargate | monetarie/backoffice | (estático) |

Build 100% **manual arm64**: `docker buildx --platform linux/arm64 → ECR → ecs register-task-definition → update-service`. Migrations via one-off `run-task` (core no capacity EC2). Mobile (Flutter) fora do ECS.

---

## 5. Rede de borda (ALB / WAF / DNS)

### Público (`monetarie.com.br`) — **só estes 4 hosts** num **único ALB internet-facing** com **WAFv2 em todo o ALB**
| Host | → TG | Serviço:porta | WAF |
|---|---|---|---|
| api-h.monetarie.com.br | monetarie-core-api | core-api:4000 | ✅ |
| ib-h.monetarie.com.br | monetarie-banking | core-banking-ui:8080 | ✅ |
| qrcode-h.monetarie.com.br | monetarie-pix-api | pix-api:4002 | ✅ |
| merchant-h.monetarie.com.br | monetarie-merchant | core-merchant-ui:8080 | ✅ |

WAFv2 REGIONAL: AWS Managed Rules (Common, KnownBadInputs, SQLi, IP-reputation, Anonymous-IP) + rate-limit. Default action 404. (Monetarie **não tem WAF** — isto é construção nova.)

### Interno (`*-h.monetarie.internal`) — **ALB interno HTTP-only, SEM cert pago**
coreadmin → `core-h.monetarie.local` (ver §8 dúvida do `.local`); cabines: `spbadmin/npcadmin/staadmin/clstadmin/pixadmin-h.monetarie.internal:8080`; APIs internas `*api-h.monetarie.internal:40xx`; backoffice; docs-h. **Derruba** o Private CA + listener 8443 da Monetarie (~US$400/mês). pix/clst/npc (que na Monetarie só existem no 8443) ganham regra na porta 80.

### DNS
Zona privada Route53 `monetarie.internal` associada à app VPC; ALIAS A por serviço → ALB interno; A para nats-1/2/3, tb-0, e MQ RTM. Público `monetarie.com.br` segue fora da AWS (CNAMEs manuais) — sem cert ACM caro.

---

## 6. Migrations & Seeds (greenfield, **sem ETL**)

DB sobe **só com migrations + seeds de referência** (sem o ETL Monetarie, que está fora do core e fora de escopo — Monetarie não tem backup legado).

**Ordem por serviço:** migrations Ecto (core 254 + npc 16 + sta 8 + clst 6 + pix 26+11 + spb 45) → seeds env-driven (core: entities→rbac→platform_admin→production→account_status_rules→efinanceira→cadoc; pix rbac+participants; spb run_all_seeds; npc/sta/clst).

**Conversões obrigatórias (Monetarie → Monetarie):**
- Config/env: `INSTITUTION_ISPB=46026562`, `INSTITUTION_CNPJ=46026562000105`, `INSTITUTION_NAME='MONETARIE SOCIEDADE DE CRÉDITO DIRETO S.A.'`, `INSTITUTION_AGENCY=0001` (confirmar), **`INSTITUTION_TYPE=SCD`**.
- **Adicionar chave faltante** `config :*, :institution_compe_code` (bug latente: hoje é lida com `fetch_env!` e nunca setada). COMPE pendente.
- Hardcodes a trocar: `bank_registry.exs:52`, `040_institutions.exs` (`:cooperativa_singular`→SCD, `fgcoop_member:false`), e-mails `@monetarie.com.br`→`@monetarie.com.br`, seeds SPB/PIX com `00512839`/SISBACEN `54106`.

**Seeds cooperativistas a PULAR (SCD não tem):** `043_guarantee_schemes` (FGCOOP), `cooperative.exs`, `cosif` FATES, `tigerbeetle` fundo FATES (id 51) + capital social, revisar `account_status_rules` `capital_shares`. Pular demo/test data (Monetarie).

**Garantia empírica:** rodar migrations do zero (replayável) + seeds num Postgres limpo, validar boot do core/pix, antes de qualquer deploy.

---

## 7. Rebrand (Monetarie → Monetarie)

- **Paleta (do logo oficial):** carvão `#18100C` + champagne `#B48F5E` + prata `#E5E4E4/#A19F9E`. Semânticas inalteradas.
- Fonte de verdade: `core/packages/shared/src/theme/tokens.css` (trocar navy/teal). Mais favicons `.svg`, inline `#003641/#00A651`, `pix/frontend/admin/style.css`.
- Mobile: `whitelabel_config.dart` + `flavors/*.env` (cores, CNPJ, nome, baseUrl), splash, ícones.
- Strings: "Cooperado"→"Cliente"/"Lojista", "Cooperativa de Crédito"→"Sociedade de Crédito Direto"; **esconder** telas `admin/views/cooperative/*`.
- **Namespace de código `Monetarie.*`/`@monetarie/*` permanece** (refactor de namespace é grande/arriscado e desnecessário — identidade é dada por config). Trocar só valores exibidos/config.

---

## 8. Guard-rails de segurança & custo

- SG do DB e datastores: ingress **só do SG do ECS** (nunca CIDR). ALB público só 80/443; WAF managed rules + rate-limit.
- Secrets **só** no Secrets Manager (`monetarie/{env}/*`), KMS per-domain. Nada hardcoded. Gerar `secret_key_base`/guardian/cloak/master novos (nunca copiar da Monetarie). mTLS BACEN = certs próprios da Monetarie.
- Custo: Aurora 0,5–4 ACU; 1 Redis `t4g.micro`; NAT único; Fargate right-sized; EC2 arm64 só p/ core-api; **sem ACM pago** (interno HTTP, público via cert grátis DNS-validated se/quando precisar).

---

## 9. Pendências externas (isoladas — não bloqueiam core/PIX)

| Item | Para quê | Quem |
|---|---|---|
| **COMPE** Monetarie | SIMBA, NPC/COMPE, bank_registry | BACEN/registro |
| **SISBACEN** Monetarie | SPB/STR, secret `bacen/sisbacen` | BACEN |
| Certs **ICP-Brasil A1** (mTLS BACEN, SPB) | PIX/SPB/e-Financeira | Monetarie (AC) |
| **IP DNS do RTM** + prefixos/portas RSFN por cabine | forward `rsfn.net.br`, egress | extrair do ambiente 192.168/16 (sem terceiros) |
| Provisionamento MQ/RTM (QM, channels por ISPB) | SPB IBM MQ | RTM (quando ativar SPB) |

## 10. Dúvidas abertas (decisão do dono)
1. `core-h.monetarie.local` — zona Route53 `.local` separada, ou foi `.internal`? (Monetarie usa `coreadmin.monetarie.internal`.)
2. `INSTITUTION_AGENCY` = 0001 confirmado?
3. Identidade de marca: app branda como a SCD, como "Grupo Monetarie", ou ambos? (site se diz "Securitizadora/paga à vista".)
4. Ambiente inicial: `-homolog` primeiro (recomendado) ou já `-prod`?

---

## 11. Estado de execução
- [x] Validação empírica AWS (rede/DX/RSFN, Aurora, permissões, CIDR livre)
- [x] Decisões travadas (rede NAT, Aurora 0,5–4, WAF, 6 cabines, branding, CIDR 10.40/16)
- [x] Este roteiro + esqueleto do monorepo
- [ ] Terraform bootstrap + network/data/dns/ecr/secrets (em escrita)
- [ ] Terraform edge (alb/waf) + compute (ecs) + messaging
- [ ] PoC de conectividade RSFN (§1.3)
- [ ] Port + rebrand + conversão de seeds por serviço (com validação empírica)
