ADR 0050 — CONNECT.Art Pattern A (Cloud Rendezvous, TURN-like)¶
- Status: Accepted
- Date: 2026-05-15
- Deciders: André Luiz Gallon (Architect/Operator)
- Supersedes: —
- Related: ADR 0049 (Satellite Federation umbrella), ADR 0051 (Pattern B P2P STUN), ADR 0053 (Cell-based Hyperscale), ADR 0054 (PQC-Everywhere), ADR 0059 (ACME-Everywhere)
- Implementation:
pkg/octopus/connect-art/(server-side; shipped Wave-1 PR #753) pkg/oobi/satellite/pattern_a/(client-side; PR-OSF-7 a shipar)
Context¶
Pattern A é o cloud-mediated rendezvous para OOBI Satellite Federation (ADR 0049). TURN-like: ambos peers (GATEWAY.Art em DCs distintos) dial out para um servidor cloud comum, que faz bridge bytes A↔B.
Funciona em 100% dos NATs — incluindo CGNAT symmetric onde Pattern B falha. Trade-off: +10-30ms latência adicional pelo relay hop.
Comparado com TURN clássico (RFC 8656):
- mTLS PQC obrigatório (não DTLS clássico)
- Identidade peer extraída do cert SAN (não credentials TURN realm-based)
- Audit chain Merkle integrada
- Multi-tenant via deployment_id (não realm-based)
Servidor já implementado em pkg/octopus/connect-art/ (Wave-1 PR #753).
Esta ADR formaliza o contrato + adiciona requisitos para o cliente
satellite que vive em pkg/oobi/satellite/pattern_a/ (a shipar PR-OSF-7).
Decisão¶
D1 — Cloud é blind tunnel (ADR 0049 D4)¶
CONNECT.Art NÃO termina mTLS interno. Cada peer faz TLS handshake com o outro peer end-to-end através do cloud. Cloud vê apenas:
- TCP-level mTLS handshake (PQC + cert chain Cisco Satellite Root CA)
- Encrypted bytes flowing through
- Counts of bytes (para billing + audit)
- Peer identities from certs (para pairing decisions)
- Pair establishment + close events
Cloud NÃO PODE: - Decrypt application payload - Modify bytes (quebraria peer-to-peer mTLS integrity) - Forge peer identity (exigiria Cisco Satellite Root CA compromise)
D2 — Pairing via deployment_id extraído do cert SAN¶
Cada peer GATEWAY.Art apresenta cert TLS cuja CN segue formato canônico:
satellite-<site_id>.<deployment_id>.satellite.tlsstress.art
Servidor pkg/octopus/connect-art/internal/auth/auth.go extrai
deployment_id + site_id via PeerIdentityFromCert. Pairing:
- Mesmo
deployment_idem ambos peers site_iddiferente (não permite self-pair)- TTL pending peer 30s
- One pair per deployment_id at a time (no triangulation)
D3 — TLS config strict (PQC hybrid + TLS 1.3 only)¶
&tls.Config{
MinVersion: tls.VersionTLS13,
MaxVersion: tls.VersionTLS13,
CipherSuites: []uint16{
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
},
CurvePreferences: []tls.CurveID{
tls.X25519MLKEM768, // PQC hybrid (ADR 0054 + ADR 0049 D3)
tls.X25519, // classical fallback DURANTE rollout
},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: ciscoSatelliteRootCA,
SessionTicketsDisabled: true,
Renegotiation: tls.RenegotiateNever,
NextProtos: []string{"h2"},
}
Nota sobre PQC fallback: ADR 0049 D3 LOCKED diz "zero fallback". A
implementação atual pkg/octopus/connect-art/internal/listener/listener.go
ainda lista X25519 classical como fallback durante rollout até todos
os peers GATEWAY.Art suportarem ML-KEM-768. PR-OSF-6 fecha esse gap.
D4 — Bridge: 2 goroutines io.Copy zero-allocation¶
// pkg/octopus/connect-art/internal/bridge/bridge.go (resumo)
func Run(ctx context.Context, pair Pair) Result {
var wg sync.WaitGroup
wg.Add(2)
closeOnce := sync.Once{}
go func() {
defer wg.Done()
n, err := io.Copy(pair.B.Conn, pair.A.Conn)
// ... close cascade
}()
go func() {
defer wg.Done()
n, err := io.Copy(pair.A.Conn, pair.B.Conn)
// ... close cascade
}()
wg.Wait()
return result
}
Properties:
- 2 goroutines = full-duplex parallelism
- io.Copy zero-allocation streaming
- First side to close triggers cascade
- ctx cancellation honored
- Final result inclui byte counts + duration + close reason
D5 — Rate limiter token bucket per source IP¶
pkg/octopus/connect-art/internal/ratelimit/:
- Default: capacity 30 burst, refill 10 RPS, GC 10min
- Per source IP — um cliente abusivo não afeta outros
- IPv4 + IPv6 unified key via
netip.Addr.Unmap()(ADR 0058 D7, PR-DS-7)
D6 — Audit chain integration¶
Cada lifecycle event vai para Merkle chain pkg/octopus/common/audit/:
{
"ts": "2026-05-14T14:23:45.123Z",
"event": "pair-established",
"deployment_id": "acme-corp-prod",
"peer_a_site_id": "dc1",
"peer_b_site_id": "dc2",
"cert_fingerprint_a": "sha256:...",
"cert_fingerprint_b": "sha256:...",
"tunnel_pattern": "a", // ADR 0049 D2
"severity": "info",
"prev_hash": "...",
"hash": "..."
}
7-year retention (legal). Tamper-evident via SHA-256 chain.
D7 — Per-cell deployment (ADR 0053)¶
Cada cell tem instância CONNECT.Art própria em
connect-${cell}.tlsstress.art (ADR 0059 D8). Selecionada via Cloudflare
KV consistent hash em deployment_id.
K8s manifest em pkg/octopus/deploy/k8s/connect-art/ com:
- replicas: 3 (HA)
- Pod anti-affinity (zone-spread)
- HPA min=3 max=30 baseado em active_pairs per pod
- ipFamilyPolicy: PreferDualStack (ADR 0058 D9, PR-DS-2)
D8 — Cliente Satellite (Pattern A) implementation¶
pkg/oobi/satellite/pattern_a/ (PR-OSF-7 a shipar) implementa:
- Dial
connect-${cell}.tlsstress.art:443com mTLS PQC - Cert SAN com formato canônico (D2)
- Reconnect com exponential backoff em disconnect
- L7 heartbeat over HTTP/2 stream 1 (ADR 0049 D1)
- State machine:
IDLE → DIALING → ESTABLISHED → DEGRADED → CLOSED - Metrics Prometheus
oobi_satellite_pattern_a_*
Alternativas consideradas¶
| Alternativa | Por que rejeitada |
|---|---|
| TURN clássico (coturn) | Não PQC, sem audit chain Merkle, realm-based auth menos seguro |
| WebRTC TURN over WebSocket | Browser-centric, overhead JS-friendly desnecessário no nosso uso |
| gRPC bidirectional stream | HTTP/2 já oferece o que precisamos sem overhead protobuf serialization |
| Custom protocol over TCP | Reinventar TURN sem ganhos; perde compat com tooling existente |
| Cloudflare Argo Tunnel | Vendor lock-in; sem control plane próprio; não multi-tenant |
Consequências¶
Positivas¶
- 100% NAT compatibility (CGNAT symmetric, dual-NAT, anything)
- Zero customer DNS work após cell selection
- Audit-rich (Merkle chain mandatory por sessão)
- Multi-tenant native via
deployment_id - HA built-in via cell replicas + multi-region failover (Wave-1 #761)
- PQC future-proof desde MVP (ADR 0054 + 0049 D3)
Negativas¶
- +10-30ms latency comparado a Pattern B P2P direto
- Cloud bandwidth cost — ambos sentidos transitam pelo cloud
- Cloud-side visibility — metadata + bytes counts (não payload), pode preocupar customers extra-paranoid (D2 mitigates via cert SAN)
- Throughput limit per cell — ~1 Gbps/pod, ~3 Gbps/cell (HPA scales mas teto)
Neutras¶
- Per-cell DNS shard — operational overhead aceitável vs blast radius
- mTLS cert rotation — automated via ACME (ADR 0059)
Compliance mapping¶
| Standard | Requirement | Where |
|---|---|---|
| NIST SP 800-208 | Crypto Agility | D3 PQC hybrid + classical fallback |
| SOC 2 CC6.1 | Logical access | D2 cert SAN allowlist + D6 audit chain |
| ISO 27001 A.13.1.1 | Network controls | D3 mTLS strict TLS 1.3 + cert chain |
| GDPR Art. 32 | State-of-the-art crypto | D3 PQC hybrid + AES-256-GCM |
| RFC 8656 | TURN (referência conceitual) | D1 cloud blind tunnel (não termina mTLS interno) |
Performance characteristics¶
| Stage | Latency |
|---|---|
| TCP SYN/SYN-ACK/ACK | RTT (5-50ms) |
| TLS 1.3 + PQC handshake | ~3-8ms server CPU |
| Pairing lookup (in-memory) | < 1ms |
| Bridge byte-pump (per byte) | ~0 (zero-copy via io.Copy) |
| Audit write (async) | ~1-2ms |
| Total handshake-to-bridge-ready | ~10-60ms (mostly RTT) |
| Component | Limit |
|---|---|
| Per pod throughput | ~1-2 Gbps |
| AWS NLB throughput | ~25 Gbps |
| TLS encryption CPU | ~10-15% overhead at 1 Gbps |
| Active pairs/pod | ~100 |
| Active pairs/cell (30 pods) | ~3000 |
Patent considerations¶
Patent claim 18 (LOCKED em ADR 0049 patent angle):
Multi-cell rendezvous service for satellite federation using post-quantum hybrid TLS 1.3 (X25519+ML-KEM-768) with cert-SAN-extracted multi-tenant identity routing and Merkle-chained pair lifecycle audit trail.
Differentiation vs prior art: - vs WebRTC TURN: no PQC, no Merkle audit - vs Cloudflare Argo Tunnel: not multi-tenant by deployment_id, lacks pair semantics - vs Tailscale DERP: similar concept but proprietary tenant model + no audit chain
Open items / future work¶
- ADR 0050-A (Wave 2) — Auto-failover entre cells (regional)
- ADR 0050-B (Q3 2026) — QUIC support (HTTP/3 over QUIC) — better mobile/CGNAT
- ADR 0050-C (Wave 4) — ML-based pair affinity (cache locality)
- ADR 0050-D — Cross-cell pair (DC-1 em us-east, DC-2 em eu-west) — currently same-cell only
Implementation status (2026-05-15)¶
| Component | Status | PR |
|---|---|---|
connect-art listener |
✅ shipped | #753 (Wave-1) |
| TLS strict config | ✅ shipped | #753 |
| Pairing table | ✅ shipped | #753 |
| Bridge io.Copy | ✅ shipped | #753 |
| Rate limiter | ✅ shipped | #753 + #829 (netip.Addr) |
| Audit chain entries | ✅ shipped | #753 + #828 (v6 fields) |
| Per-cell K8s manifests | ✅ shipped | #753 + #824 (PreferDualStack) |
| Cert lifecycle (ACME) | ✅ shipped | #813 (Wave-ACME-3) |
| Hot-reload cert | ✅ shipped | #815 (Wave-ACME-5) |
| Prometheus cert metrics | ✅ shipped | #816 (Wave-ACME-6) |
| Satellite client lib | ⏳ pending | PR-OSF-7 |
| L7 heartbeat integration | ⏳ pending | PR-OSF-5 |
| PQC hybrid no-fallback | ⏳ pending | PR-OSF-6 (currently TLS lib supports both) |
Cross-references¶
- ADR 0049 — Satellite Federation umbrella
- ADR 0051 — Pattern B P2P STUN (a redigir PR-OSF-3)
- ADR 0053 — Cell-based Hyperscale
- ADR 0054 — PQC-Everywhere
- ADR 0058 — Dual-Stack
- ADR 0059 — ACME-Everywhere
- Pattern A deep dive (pkg/octopus/docs)
- RFC 8656 — Traversal Using Relays around NAT (TURN)
- Source: pkg/octopus/connect-art/