Skip to content

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:

  1. Mesmo deployment_id em ambos peers
  2. site_id diferente (não permite self-pair)
  3. TTL pending peer 30s
  4. 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:443 com 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