ADR 0058 — OCTOPUS IPv4/IPv6 Dual-Stack Mandate¶
- Status: Accepted
- Date: 2026-05-15
- Deciders: André Luiz Gallon (Architect/Operator)
- Supersedes: —
- Related: ADR 0007 (Public-Internet Realism), ADR 0050 (CONNECT.Art rendezvous), ADR 0051 (P2P STUN), ADR 0053 (Cell-based Hyperscale), ADR 0054 (PQC-Everywhere), ADR 0059 (ACME-Everywhere)
- Memory:
project_octopus_dual_stack_ipv6_mandate_2026_05_15.md
Context¶
A depleção de IPv4 chegou ao ponto crítico em vários mercados-alvo OCTOPUS:
- Brasil: ISPs residenciais (Vivo, Claro, Oi) já entregam IPv6-only com NAT64 para clientes residenciais; CGNAT no fallback é caro e degradado
- Índia: Reliance Jio (~450M users) é majoritariamente IPv6-only puro
- China: política governamental 2025+ exige IPv6-only em novos deploys de operadora; CGNAT está sendo desativada
- Nigéria + África emergente: MTN/Airtel migrando para IPv6 nativo
- Mobile: T-Mobile US, Verizon, Reliance Jio operam 464XLAT (IPv6-only + CLAT no device) — mas peers fora dessa rede ainda precisam IPv6 first-class
OCTOPUS clients (GATEWAY.Art) rodam no DC do customer — frequentemente atrás do ISP residencial ou enterprise dele. Suportar apenas IPv4 significa:
- CGNAT-only fallback — degradação severa de hole-punching (Pattern B)
- Exclusão de mercados — Brasil/Índia/China inteiros perdidos
- Lock-in em NAT64 carrier-side — solução que ISPs estão desativando
- Custo IPv4 — AWS/GCP/Azure cobram ~$0.005/IP/hora; multiplicado por centenas de cells em Wave-3 hyperscale, vira gasto significativo
- Compliance future — alguns mandatos governamentais já exigem IPv6 (US Federal: OMB M-21-07, "IPv6-only by FY2025")
Auditoria do estado atual (2026-05-15) encontrou 15 gaps de IPv6 no codebase + infra Terraform + K8s manifests + docs.
A janela para fechar esses gaps antes de Wave-4 hyperscale (50k+ clientes) é agora.
Decisão¶
OCTOPUS adota dual-stack IPv4+IPv6 com paridade L3 em todo o projeto, sem fallback NAT64 obrigatório. IPv6 é first-class everywhere.
D1 — Paridade L3 total¶
IPv4 e IPv6 são tratados como cidadãos iguais em todos os componentes:
- Cloud cells (VPC dual-stack AWS + GCP + Azure)
- K8s Services (
ipFamilyPolicy: PreferDualStack,ipFamilies: [IPv4, IPv6]) - DB schemas (Postgres
INET, ClickHouseIPv6/IPv4, Gonetip.Addr) - Cert SAN (inclui v4 IP, v6 IP, e DNS quando aplicável)
- Rate limiter + ACL parsers entendendo CIDR v6
- Audit chain entries com campos IP v6-capable
- DNS records — AAAA paralelo a A para todos os anycast endpoints
- Edge LB / Cloudflare Workers — dual-stack listeners
- Customer Dashboard "connectivity test" tool — testa v4 + v6
D2 — IPv6-only client deve funcionar end-to-end¶
GATEWAY.Art em DC cliente atrás de ISP IPv6-only puro DEVE funcionar identicamente a IPv4. Isso significa:
- Pattern A (CONNECT.Art rendezvous) aceita conexão IPv6 puro
- Pattern B (STUN-coord) descobre srflx v6, hole-punch v6-v6
- PQC mTLS sobre v6 sem regressão
- Anycast edge dual-stack (Cloudflare Magic Transit cobre nativamente)
- Sem dependência de NAT64 carrier-side (não-suportada em prod)
NAT64 fica como escape hatch on-edge apenas para customers que explicitamente declaram cenário mixed legacy v4-only-server. Nunca mandatory.
D3 — Listener defaults: :port, não 0.0.0.0:port¶
Todo listener Go DEVE bindar em :port (que resolve para [::]:port
em dual-stack systems via stdlib) ao invés de 0.0.0.0:port.
// ❌ REJEITADO em PR review
net.Listen("tcp", "0.0.0.0:8443")
// ✅ ACEITO
net.Listen("tcp", ":8443")
Existing code: já está mostly correto (audit 2026-05-15 não achou listener
0.0.0.0: em prod paths). Mantém via PR reviewer checklist.
D4 — Schemas DB: tipos IP v6-capable¶
Toda coluna DB que armazena endereço IP DEVE usar tipo v6-capable:
| Banco | Tipo v6-capable | Tipo a evitar |
|---|---|---|
| Postgres | INET ou CIDR |
varchar(15), inet4-only |
| ClickHouse | IPv6 ou IPv4/IPv6 Union |
String |
| Go in-memory | netip.Addr (Go 1.18+) |
net.IP legacy (mutable, 16-byte ambiguity) |
netip.Addr é particularly important porque é immutable + canonicaliza
v6 representations + suporta zone scopes.
D5 — Cert SAN: v4+v6+DNS quando aplicável¶
ACME issuer (PR-ACME-2 #805 MERGED) emite cert para FQDNs, mas quando um serviço tem IP literal em audit logs / connection tracking, esse IP deve ter SAN entry separado para v4 + v6.
Para cell-aware endpoints (Wave-3 #777-#786), cert-manager Certificate CR template inclui:
spec:
dnsNames:
- connect-us-east-1-a.tlsstress.art
ipAddresses:
- "192.0.2.1" # IPv4 anycast
- "2001:db8::1" # IPv6 anycast
PR-ACME-3 (#813 MERGED) ships com dnsNames apenas; adicionar
ipAddresses para v6 SAN é parte da Dual-Stack PR-5 (cert SAN audit).
D6 — Audit chain JSON: campos IP aceitam v6¶
Audit entries da Merkle chain (ADR 0048) gravam IP do peer. Schema atualizado:
{
"event": "pair-established",
"peer_ip": "2001:db8::1", // ← v6 OK
"peer_ip_family": "ipv6", // ← NOVO field para query/filter
"...": "..."
}
pkg/octopus/common/audit/types.go precisa atualizar struct fields para
suportar v6 strings sem truncation.
D7 — Rate limiter + ACL: netip.Addr-backed¶
Rate limiter token bucket (CONNECT.Art pkg/octopus/connect-art/internal/ratelimit/)
key é source IP. Migrar de net.IP para netip.Addr evita confusão
v4-mapped-v6 (::ffff:1.2.3.4 é o mesmo bucket que 1.2.3.4 apenas com
canonicalization). netip.Addr.Unmap() resolve.
CIDR allowlists para deployment_id authorization seguem mesma migração.
D8 — DNS: AAAA paralelo a A em todos os anycast endpoints¶
Toda zona Cloudflare/Route53 com record A para endpoint público DEVE ter AAAA paralelo:
connect-us-east-1-a.tlsstress.art. A 192.0.2.1
connect-us-east-1-a.tlsstress.art. AAAA 2001:db8::1 ← OBRIGATÓRIO
Anycast IPs reservados em ranges /23 (AWS Global Accelerator), /29 (Cloudflare Magic Transit) — todos têm v6 native.
D9 — K8s Services: PreferDualStack default¶
Todo K8s Service novo DEVE declarar:
spec:
ipFamilyPolicy: PreferDualStack
ipFamilies:
- IPv4
- IPv6
Existing Services serão atualizados em Dual-Stack PR-2 (K8s sweep).
CNI deve suportar dual-stack — flannel default k3s/RKE2 NÃO suporta. Wave-PR-4 migra para Calico OR Cilium.
D10 — Compliance gate: PR reviewer checklist¶
Toda PR que toque networking, schemas DB, K8s manifests, ou listeners
DEVE passar pelo checklist pkg/octopus/docs/07-development/pr-checklist-dual-stack.md
(este wave entrega).
Critérios de rejeição automática:
1. Listener 0.0.0.0:port em produção
2. Schema com varchar(15) ou inet4 para IP
3. K8s Service sem ipFamilyPolicy
4. Cert SAN com IP v4-only quando v6 disponível
5. CIDR parser net.IP legacy ao invés de netip.Addr
6. Falta AAAA paralelo a A em zona DNS
7. Smoke test v4-only sem cobertura v6 paralela
Wave de implementação¶
11 PRs estimados, 18-22 dias úteis. Ordem (DAG):
| # | PR | Esforço | Dependencies |
|---|---|---|---|
| PR-DS-1 (este) | ADR 0058 + PR reviewer checklist | 1d | — |
| PR-DS-2 | K8s Services PreferDualStack + DNS AAAA records | 1d | PR-DS-1 |
| PR-DS-3 | Terraform VPCs IPv6 CIDR (AWS+GCP+Azure) | 2-3d | PR-DS-2 |
| PR-DS-4 | CNI Calico/Cilium dual-stack migration | 2-3d | PR-DS-3 |
| PR-DS-5 | Cert SAN v6 audit + Certificate CR update | 1d | PR-DS-3 |
| PR-DS-6 | DB schemas INET/IPv6 audit + migrations | 1d | — |
| PR-DS-7 | Rate limiter + ACLs netip.Addr migration | 1d | — |
| PR-DS-8 | Edge AAAA + Cloudflare Workers v6 listener | 2d | PR-DS-2 |
| PR-DS-9 | Smoke tests v6-only paths (CONNECT.Art + STUN-coord) | 1d | PR-DS-4 |
| PR-DS-10 | Docs sweep "IPv6 first-class" linguagem (25+ docs) | 2d | — |
| PR-DS-11 | Dashboard connectivity test tool v4+v6 | 2d | PR-DS-9 |
Crítico path: PR-DS-1 → PR-DS-2 → PR-DS-3 → PR-DS-4 → PR-DS-9 = ~10 dias úteis. Outros PRs paralelos em ~10 dias úteis.
Alternativas consideradas¶
| Alternativa | Por que rejeitada |
|---|---|
| IPv4-only + CGNAT | Exclui Brasil/Índia/China; ISPs desativando NAT64 carrier-side |
| IPv6-only puro | Quebra customers legacy v4-only DCs (ainda majoritário em US enterprise) |
| Dual-stack mas v6 second-class | Auditoria já mostrou 15 gaps; manutenção contínua de paridade exige LOCKED commitment |
| NAT64 mandatory on-edge | Operacionalmente complexo, hidden state, debugging pesadelo |
| 464XLAT carrier-side | Não controlamos o carrier; cliente IPv6-only puro continua falhando se 464XLAT down |
Consequências¶
Positivas¶
- Markets unlocked: Brasil/Índia/China/África emergente acessíveis nativamente
- Custo cloud reduzido: IPv6 é grátis nos 3 hyperscalers; IPv4 custa $0.005/IP/h (AWS) = ~$3.60/IP/mês × ~30 cells × 4 IPs = ~$430/mês evitado em Wave-4
- Future-proof: alinhado com OMB M-21-07 + outras políticas governamentais
- Cloudflare/AWS edge native v6 desde 2014
- PQC mTLS funciona igual — não há regressão criptográfica
Negativas¶
- CNI migration risk — Calico/Cilium switch pode quebrar production cluster (mitigação: roll forward em staging primeiro, 30d soak time)
- DB migrations — Postgres
INETé compatível com strings v4, mas driver-level checks podem precisar update (mitigação: Drizzle migrations em backward-compat mode) - Docs sweep volume — 25+ docs para reescrever (mitigação: PR-DS-10 faz em uma passada via script + manual review)
Neutras¶
- IPv6 addresses são 8x mais longos em logs (mitigação: ferramentas modernas lidam bem; cardinality Prometheus já bounded)
- Operator training: equipe precisa familiarizar com v6 CIDR notation (mitigação: cheat sheet em runbook)
Compliance mapping¶
| Standard | Requirement | Where |
|---|---|---|
| OMB M-21-07 | "IPv6-only by FY2025" for US Federal | D1+D2 enable FedRAMP customers |
| OMB M-21-07 §III.B | Eliminate dependency on IPv4 | NAT64 NOT mandatory (D2) |
| RIPE-690 | Recommendations on IPv6 deployment | Followed throughout |
| RFC 8064 | Stable IPv6 IIDs | netip.Addr canonicalization (D7) |
| RFC 9099 | IPv6 security | rate limiter v6-aware (D7) |
| SOC 2 CC6.1 | Logical access controls | ACLs accept v6 CIDR (D7) |
| ISO 27001 A.13.1 | Network security | dual-stack uniform posture (D1) |
Patent considerations¶
A combinação de:
- Multi-cloud anycast dual-stack edge
- Per-cell IPv6 allocation strategy via Terraform
- PQC mTLS sobre IPv6 com rate limiter netip.Addr-backed
…não é patenteavel em si (são componentes standards). Mas a integração end-to-end via OCTOPUS architecture pode integrar Patent Family E (federation patterns) como claim adicional. Avaliar com IP counsel após Wave completo.
Open items / future work¶
- ADR 0058-A (Q3 2026) — IPv6-only-only mode (drop IPv4 entirely) para customers que querem zero v4 surface. Não escopo do Wave atual.
- ADR 0058-B (Q4 2026) — Happy Eyeballs v2 (RFC 8305) tuning para customers com path degradation em uma family
- 464XLAT awareness — quando customer roda nesta config, ajustar STUN candidate gathering para evitar CLAT-pinned addresses como srflx
Memory + cross-references¶
Update MEMORY.md index entry para apontar este ADR como AUTHORITATIVE:
Memory:
project_octopus_dual_stack_ipv6_mandate_2026_05_15.md← memo of decision ADR:docs/ADR/0058-octopus-dual-stack-ipv4-ipv6.md← this file (canonical) Checklist:pkg/octopus/docs/07-development/pr-checklist-dual-stack.md← reviewer enforcement
Cross-references: - ADR 0050 (CONNECT.Art) — listener bind semantics D3 applies - ADR 0051 (P2P STUN) — IPv6 srflx in XOR-MAPPED-ADDRESS already supported (Go stdlib stun-coord) - ADR 0053 (Cell-based Hyperscale) — VPC IPv6 CIDR per cell (D9) - ADR 0054 (PQC-Everywhere) — TLS curves operate transparent over v4 OR v6 - ADR 0059 (ACME-Everywhere) — Cert SAN includes IP types (D5)