F2 — Cell-0 no Hetzner: arquitetura técnica, interconexões e runbook¶
Escopo: documento profundo (HLD + LLD + diagramas + scripts + manuais) do OCTOPUS F2 — auto-provisionamento de clientes materializado no cell-0 (Hetzner Cloud), e de suas interconexões com AWS e Cloudflare.
Status: 🟢 LIVE (2026-06-13). Onboarding automático provado fim-a-fim. Custo incremental: ~$12-15/mo (Hetzner), zero a mais na AWS.
ADRs relacionados: 0053 (cell-based hyperscale + Wave-1 cell-0 substrate), 0056 (auto-provisioning saga), 0050/0051 (CONNECT.Art / STUN-coord), 0102 (auth). Memória:
project_f2_onboarding_wiring_2026_06_13.
1. Sumário executivo¶
O F2 fecha o "meio" da cadeia comercial (signup → compra → provisionamento → produção). Um pagamento liquidado no customer-app (AWS App Runner) dispara, via HTTPS+HMAC, o Provisioning Orchestrator rodando no cell-0 (um VPS Hetzner com K3s), que executa o OnboardingWorkflow (Temporal) — um saga de 12 atividades com compensação LIFO — provisionando o tenant do cliente (isolamento Postgres RLS), emitindo o certificado de cliente (cert-manager), registrando quota e slots de data-plane, e gerando o JWT de onboarding.
Três nuvens, três papéis:
| Nuvem | Papel | Componentes |
|---|---|---|
| AWS | Control-plane SaaS sempre-online | customer-app (App Runner), RDS (ledger UTXO), S3 (TBI/F1), admin-console |
| Cloudflare | DNS + borda | app.tlsstress.art (proxied), f2.tlsstress.art (DNS-only → cell) |
| Hetzner | Cell-0 data/control de provisionamento | K3s, Traefik, cert-manager, Temporal+Postgres, Provisioner, cell control DB |
2. HLD — Topologia geral¶
graph TB
subgraph Internet
CUST([Cliente])
end
subgraph CF["Cloudflare (DNS + borda)"]
APPDNS["app.tlsstress.art<br/>(proxied)"]
F2DNS["f2.tlsstress.art A 204.168.178.210<br/>(DNS-only / cinza)"]
end
subgraph AWS["AWS us-east-1 (control plane SaaS)"]
AR["customer-app<br/>App Runner :3200<br/>(Next 16)"]
RDS[("RDS db.t4g.micro<br/>UTXO ledger<br/>mint @ checkout")]
S3[("S3 bootstrap-artifacts<br/>TBI / F1 download")]
STRIPE{{Stripe}}
end
subgraph HETZNER["Hetzner cx33 — cell-0 (hel1, 204.168.178.210)"]
FW[["Cloud Firewall 11131764<br/>allow 22/80/443/ICMP · drop 6443/10250"]]
subgraph K3S["K3s v1.35.5 (single-node)"]
TR["Traefik ingress<br/>:80 / :443"]
subgraph NSO["ns octopus"]
PROV["provisioner<br/>Temporal worker + /trigger :8080"]
end
subgraph NST["ns temporal"]
TMP["Temporal frontend :7233<br/>(auto-setup 1.29.6.1)"]
PG[("Postgres 16<br/>db: temporal · cell")]
end
subgraph NSC["ns cert-manager"]
CM["cert-manager 1.20.2"]
CAISS["ClusterIssuer<br/>octopus-provisioning-ca"]
LEISS["ClusterIssuer<br/>letsencrypt-prod"]
end
subgraph NSTEN["ns tenants"]
CERTS["Certificates por tenant<br/>(client certs)"]
end
end
end
CUST -->|signup/pay| APPDNS --> AR
STRIPE -->|checkout.session.completed| AR
AR -->|"applyTier (mint quota)"| RDS
AR -->|"enqueueOnboarding<br/>HMAC POST"| F2DNS -->|443 TLS| FW --> TR
TR -->|"/trigger/onboarding"| PROV
PROV -->|ExecuteWorkflow| TMP
TMP <-->|sql| PG
PROV -->|"IssueClientCert"| CM --> CAISS --> CERTS
PROV -->|"tenant/slots/quota"| PG
LEISS -.->|"cert público f2.*"| TR
CUST -->|"pay→download TBI (F1)"| S3
Propriedade de segurança fundamental: o enqueueOnboarding do customer-app
degrada para log se o trigger falhar (cell offline, TLS, etc.) — o checkout
nunca vira 5xx. F2 é aditivo e fail-safe.
3. HLD — Fluxo de onboarding (sequência)¶
sequenceDiagram
autonumber
participant S as Stripe
participant A as customer-app (AWS)
participant CF as Cloudflare DNS
participant T as Traefik (Hetzner)
participant P as Provisioner
participant W as Temporal
participant DB as Cell DB
participant CMG as cert-manager
S->>A: checkout.session.completed (pago)
A->>A: handleCheckoutCompleted → applyTier (mint quota no RDS)
A->>CF: resolve f2.tlsstress.art
A->>T: POST /trigger/onboarding (HMAC X-Provisioning-Signature)
T->>P: rota TLS (cert LE) → :8080
P->>P: verifica HMAC (constant-time)
P->>W: StartWorkflow OnboardingWorkflow(input)
Note over W,DB: saga 12 atividades (compensação LIFO em ctx desconectado)
W->>P: KYC (antifraud) → Mint id → AllocateCell(hel1-0)
W->>CMG: IssueClientCert → Certificate CR → assina (CA issuer)
W->>DB: ProvisionTenant (RLS) · RecordQuota · ReserveSlot×2
W->>P: GenerateOnboardingJWT (Ed25519) · WelcomeEmail · Audit
W-->>P: COMPLETED {deployment_id, cert_handle, jwt, dashboard_url}
P-->>A: 202 {workflow_id}
4. LLD — Componentes¶
4.1 Host (Hetzner cx33)¶
| Item | Valor |
|---|---|
| Server ID | 140682072 |
| Tipo | cx33 (4 vCPU / 7.6 GB / 75 GB) |
| OS | Ubuntu 24.04.4 LTS, x86_64 |
| Região | hel1 (Helsinki) |
| IPv4 / IPv6 | 204.168.178.210 / 2a01:4f9:c013:3c28::/64 |
| Rede privada | 172.20.20.2 |
| SSH | chave ed25519 ~/.ssh/tlsstress_f2_hetzner, root@ |
Hardening (/etc/ssh/sshd_config.d/99-tlsstress-hardening.conf):
PasswordAuthentication no, KbdInteractiveAuthentication no,
PermitRootLogin prohibit-password. + fail2ban + unattended-upgrades.
4.2 Firewall (Hetzner Cloud Firewall — Terraform)¶
Módulo IaC: platform/terraform/f2-hetzner-cell0/ (firewall id 11131764).
Default-deny inbound; servidor referenciado read-only (data "hcloud_server")
para Terraform nunca recriar.
| Direção | Porta | Origem | Motivo |
|---|---|---|---|
| in | 22/tcp | 0.0.0.0/0,::/0 ¹ |
SSH (key-only) |
| in | 80/tcp | any | ACME HTTP-01 + redirect |
| in | 443/tcp | any | trigger HTTPS + ingress |
| in | ICMP | any | diagnóstico |
| in | 6443, 10250, 8472, 2379-2380, resto | — | DROP |
¹ SSH aberto por uplink dinâmico 4G/CGNAT do operador; host é key-only.
Restringir via -var ssh_source_ips quando houver IP fixo/VPN.
Verificado pós-apply: 6443/10250 filtradas da internet; 22/443 abertas.
4.3 Kubernetes (K3s)¶
v1.35.5+k3s1, single-node, instalado com
--tls-san 204.168.178.210 --write-kubeconfig-mode 0644. Ingress = Traefik
(bundled). API 6443 protegida pelo firewall (não pública). kubectl roda no
host via SSH.
4.4 PKI (cert-manager 1.20.2) — dois issuers¶
graph LR
SS["ClusterIssuer<br/>selfsigned-bootstrap"] --> CACERT["Certificate<br/>octopus-provisioning-ca<br/>(ns cert-manager, ECDSA-384, 10y)"]
CACERT --> CAISS["ClusterIssuer (CA)<br/>octopus-provisioning-ca-issuer"]
CAISS --> CLIENT["client certs por tenant<br/>(ns tenants)"]
LE["ClusterIssuer (ACME)<br/>letsencrypt-prod<br/>HTTP-01 via Traefik"] --> PUBCERT["cert público<br/>f2-tlsstress-art-tls"]
octopus-provisioning-ca-issuer(CA interna) → emite os certs de cliente queIssueClientCertcria. Substitui o HashiCorp Vault (ADR-0053-H4).letsencrypt-prod(ACME HTTP-01) → emite o cert público def2.tlsstress.artpara o trigger (issuer Let's Encrypt, auto-renova).
4.5 Temporal + Postgres (ns temporal)¶
| Recurso | Detalhe |
|---|---|
| Postgres | postgres:16-alpine, Deployment + PVC 10Gi + Service postgres, user temporal (secret temporal-pg) |
| Databases | temporal, temporal_visibility (Temporal) + cell (control plane do F2) |
| Temporal | temporalio/auto-setup:1.29.6.1 (DB=postgres12, schema+default ns automáticos) |
| Frontend | Service temporal-frontend.temporal.svc.cluster.local:7233 |
⚠️ O frontend faz bind no IP do pod, não em loopback → use o Service DNS ou o
pod IP (--address localhost é recusado).
4.6 Cell control DB (database cell, ADR-0053 Wave-1)¶
Migração idempotente embarcada no binário (internal/cell/migrations/0001_cell_control.sql),
aplicada no boot do provisioner (Manager.Migrate).
erDiagram
tenants {
text deployment_id PK
text cell_id
text customer_email
text status "provisioning|provisioned|rollback_pending|released"
bigint tokens_included
timestamptz created_at
timestamptz updated_at
}
slots {
text deployment_id PK
text kind PK "connect-art|stun-coord"
text cell_id
timestamptz reserved_at
timestamptz released_at "NULL=ativo"
}
tenant_data {
text deployment_id PK
text k PK
jsonb v
text cell_id
}
Isolamento = Postgres RLS shared-schema (escala a 10k tenants/cell):
tenant_data tem ENABLE/FORCE ROW LEVEL SECURITY + policy
USING (deployment_id = current_setting('octopus.tenant')). O control-plane
(provisioner) roda privilegiado; o data-plane (CONNECT.Art/STUN-coord/runtime
do tenant) conecta com o role tenant_dataplane (NOSUPERUSER NOBYPASSRLS),
para o qual a RLS é enforçada. Provado live: leitura cross-tenant sob esse role = 0 linhas.
Slot = entrada de admissão (não recurso de runtime escasso): CONNECT.Art/
STUN-coord leem slots para admitir um deployment_id.
4.7 Provisioner (ns octopus)¶
| Recurso | Detalhe |
|---|---|
| Imagem | tlsstress/provisioner:0.1.1 (scratch + CA bundle, imagePullPolicy: Never) |
| Deployment | octopus/provisioner (1 réplica, runAsNonRoot 65532, probes /healthz:8080) |
| ServiceAccount | provisioner |
| RBAC | Role provisioner-pki (ns tenants): certificates create/get/list/delete + secrets get/delete |
| Service | provisioner:8080 |
| Trigger | POST /trigger/onboarding (HMAC-SHA256, montado só se PROVISIONING_TRIGGER_SECRET setado) |
| Worker | registra OnboardingWorkflow no task queue octopus-provisioning |
Env (Deployment + secret provisioner-env):
PROVISIONER_MODE=real
PROVISIONER_PKI=certmanager
PROVISIONER_CERT_NAMESPACE=tenants
PROVISIONER_PKI_ISSUER=octopus-provisioning-ca-issuer
PROVISIONER_CERT_DOMAIN=satellite.tlsstress.art
OCTOPUS_CELL_ID=hel1-0
TEMPORAL_ADDR=temporal-frontend.temporal.svc.cluster.local:7233
HTTP_ADDR=:8080
CELL_DATABASE_URL=postgres://temporal:<pw>@postgres.temporal.svc.cluster.local:5432/cell?sslmode=disable # secret
PROVISIONING_TRIGGER_SECRET=<hmac 64-hex> # secret
PROVISIONER_JWT_ED25519_SEED=<base64 32-byte seed> # secret
Contrato de honestidade (fail-closed): cada activity é real onde a infra
existe, ou retorna ErrNotProvisioned (nunca fake). RefundStripe fail-closa
com escalação CRÍTICA (não há Stripe client no provisioner).
4.8 OnboardingWorkflow (Temporal, saga)¶
12 atividades + compensação LIFO em workflow.NewDisconnectedContext
(cancelamento não aborta rollback); stack limpa após o ponto de no-return
(welcome email). RetryPolicy: 5 tentativas, backoff exp.
| # | Activity | Compensação | Backend |
|---|---|---|---|
| 1 | KYCCheck | — | antifraud heurístico |
| 2 | MintDeploymentID | MarkDeploymentIDRolledBack | crypto-rand dpl_<hex> |
| 3 | AllocateCell | ReleaseCellAllocation | cell (hel1-0) |
| 4 | IssueClientCert | RevokeClientCert | cert-manager |
| 5 | ProvisionTenant | MarkTenantRollbackPending | cell DB (RLS) |
| 6 | AllocateTokenQuota | BurnTokenQuota | cell DB (tokens_included) ² |
| 7 | ReserveConnectArtSlot | ReleaseConnectArtSlot | cell DB (slots) |
| 8 | ReserveStunCoordSlot | ReleaseStunCoordSlot | cell DB (slots) |
| 9 | GenerateOnboardingJWT | — | Ed25519 (seed) |
| 10 | SendWelcomeEmail | — (no-return) | Postmark (best-effort) |
| 11 | AppendAuditChain | — | log estruturado |
| 12 | NotifyAdminHighValue | — | Slack webhook (best-effort) |
² Quota: o mint autoritativo é no checkout (customer-app→RDS UTXO). A activity
registra a entitlement no cell (tenants.tokens_included), não é 2º mint.
5. Interconexões Hetzner / AWS / Cloudflare¶
5.1 Matriz de rede / portas¶
| De | Para | Porta/Proto | Auth | Notas |
|---|---|---|---|---|
| Internet | App Runner | 443 | sessão / license-JWT | via Cloudflare (app.* proxied) |
| App Runner | f2.tlsstress.art (Hetzner) |
443 HTTPS | HMAC-SHA256 body | cert Let's Encrypt; CF DNS-only |
| App Runner | RDS | 5432 | IAM/secret | mint de quota no checkout (privado VPC) |
| cert-manager (Hetzner) | Let's Encrypt | 443 (out) | ACME | HTTP-01 (porta 80 inbound p/ challenge) |
| Operador | Hetzner | 22 | SSH key | key-only |
| Operador (CF API) | Cloudflare API | 443 | token Zone:DNS:Edit | gerência do registro f2 |
5.2 Cadeia de confiança (PKI)¶
graph TB
LE["Let's Encrypt (público)"] --> F2C["f2.tlsstress.art TLS"] --> ARFETCH["fetch do App Runner<br/>(verifica cadeia pública)"]
ROOT["octopus-provisioning-ca (interna)"] --> CLI["client cert do deployment<br/>(satellite.*.tlsstress.art)"]
HMAC["PROVISIONING_TRIGGER_SECRET<br/>(compartilhado App Runner ↔ provisioner)"] --> TRIG["autentica o /trigger"]
- TLS público (App Runner→cell): Let's Encrypt → confiável pela cadeia pública padrão.
- HMAC (App Runner↔provisioner): segredo compartilhado, assina o body do trigger.
- Client cert (por tenant): CA interna
octopus-provisioning-ca(não público).
5.3 Por que esta topologia (custo + arquitetura)¶
- F2 fora da AWS (Hetzner ~$12/mo) preserva o cost-pause da AWS (pré-receita).
- Control-plane SaaS sempre-online fica na AWS (App Runner) + Cloudflare (borda).
- O cell phone-home reverso: o customer-app chama o cell (push), evitando abrir o RDS para o Hetzner (fronteira de segurança limpa — quota fica na AWS).
6. Scripts¶
Pré-requisitos:
~/.ssh/tlsstress_f2_hetzner,~/.config/tlsstress/cloudflare.token(Zone:DNS:Edit),~/.config/tlsstress/hcloud.token(Hetzner). Build do binário precisa cross-compile local (Mac arm64 → linux/amd64); a imagem é construída no cell (nerdctl+buildkit) por causa do uplink throttled + Docker do Mac.
6.1 Firewall (Terraform)¶
cd platform/terraform/f2-hetzner-cell0
export HCLOUD_TOKEN=$(tr -d '[:space:]' < ~/.config/tlsstress/hcloud.token)
terraform init && terraform plan && terraform apply
6.2 Build + load da imagem do provisioner (no cell)¶
# 1) cross-compile local (módulo pkg/octopus)
cd pkg/octopus
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" \
-o /tmp/f2img/provisioner ./provisioning-orchestrator/cmd/provisioner
# 2) sobe (comprimido — uplink throttled) + Dockerfile.local (alpine+ca-certs+COPY)
scp -C -i ~/.ssh/tlsstress_f2_hetzner /tmp/f2img/{provisioner,Dockerfile.local} \
root@204.168.178.210:/root/f2build/
# 3) buildkitd no cell (runc no PATH + host net) + build no namespace k8s.io
ssh -i ~/.ssh/tlsstress_f2_hetzner root@204.168.178.210 '
export PATH=/var/lib/rancher/k3s/data/current/bin:$PATH
pgrep buildkitd >/dev/null || nohup buildkitd --oci-worker-net=host >/var/log/buildkitd.log 2>&1 &
sleep 2
cd /root/f2build
nerdctl --address /run/k3s/containerd/containerd.sock --namespace k8s.io \
build -t tlsstress/provisioner:0.1.1 -f Dockerfile.local .'
6.3 Deploy (manifests + secret)¶
# manifests (SA + RBAC tenants + Deployment + Service)
ssh ... 'cat > /root/f2build/provisioner.yaml' < platform/k8s/f2-cell0/provisioner.yaml
ssh ... 'kubectl apply -f /root/f2build/provisioner.yaml'
# secret (gerado no cell — DSN reusa a senha do temporal-pg)
ssh ... '
PGPW=$(kubectl -n temporal get secret temporal-pg -o jsonpath="{.data.password}" | base64 -d)
kubectl -n octopus create secret generic provisioner-env \
--from-literal=CELL_DATABASE_URL="postgres://temporal:${PGPW}@postgres.temporal.svc.cluster.local:5432/cell?sslmode=disable" \
--from-literal=PROVISIONING_TRIGGER_SECRET="$(openssl rand -hex 32)" \
--from-literal=PROVISIONER_JWT_ED25519_SEED="$(openssl rand 32 | base64 -w0)" \
--dry-run=client -o yaml | kubectl apply -f -
kubectl -n octopus rollout status deploy/provisioner'
6.4 DNS (Cloudflare API)¶
CF=$(tr -d '[:space:]' < ~/.config/tlsstress/cloudflare.token); API=https://api.cloudflare.com/client/v4
ZID=$(curl -s -H "Authorization: Bearer $CF" "$API/zones?name=tlsstress.art" | jq -r .result[0].id)
curl -s -X POST -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
"$API/zones/$ZID/dns_records" \
-d '{"type":"A","name":"f2","content":"204.168.178.210","ttl":300,"proxied":false}'
6.5 Cert público + ingress (cert-manager LE + Traefik)¶
kubectl apply de: ClusterIssuer letsencrypt-prod (ACME HTTP-01 via Traefik) +
Ingress octopus/provisioner-trigger (host f2.tlsstress.art, path
/trigger/onboarding, cert-manager.io/cluster-issuer: letsencrypt-prod,
tls.secretName: f2-tlsstress-art-tls). (Manifests completos: §4.4/§4.7.)
6.6 Disparar onboarding (HMAC assinado)¶
SECRET=$(kubectl -n octopus get secret provisioner-env -o jsonpath='{.data.PROVISIONING_TRIGGER_SECRET}' | base64 -d)
BODY='{"stripe_session_id":"cs_x","stripe_customer_id":"cus_x","email":"a@b.c","name":"X","country_code":"US","package_slug":"pro-monthly","amount_cents":100000}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')
curl -s -X POST https://f2.tlsstress.art/trigger/onboarding \
-H "X-Provisioning-Signature: $SIG" -H "Content-Type: application/json" -d "$BODY"
# → 202 {"workflow_id":"onboarding-cs_x"}
6.7 Cutover (AWS App Runner — env vars)¶
⚠️ aws apprunner update-service é bloqueado pelo gate de permissão local
(proteção do app de prod). Fazer no console (Configuration → Edit → Env vars):
PROVISIONING_TRIGGER_URL=https://f2.tlsstress.art/trigger/onboarding +
PROVISIONING_TRIGGER_SECRET=<valor do secret>. update-service substitui a
config inteira → preservar os 33 env vars + 12 secrets + image.
7. Runbook operacional¶
7.1 Redeploy de nova versão do provisioner¶
- Cross-compile (§6.2.1) → upload (§6.2.2) → build
:0.1.N(§6.2.3). kubectl -n octopus set image deploy/provisioner provisioner=tlsstress/provisioner:0.1.N.kubectl -n octopus rollout status deploy/provisioner+ checar logs (worker starting on task_queue).
7.2 Rotacionar o PROVISIONING_TRIGGER_SECRET¶
NEW=$(openssl rand -hex 32);kubectl -n octopus create secret generic provisioner-env --from-literal=PROVISIONING_TRIGGER_SECRET=$NEW ... (re-aplicar com os 3 valores) | kubectl apply -f -.kubectl -n octopus rollout restart deploy/provisioner.- Atualizar
PROVISIONING_TRIGGER_SECRETno App Runner (console) → redeploy. - (Os dois lados devem bater; durante a troca, o enqueue degrada para log.)
7.3 Verificar o cutover (AWS — leitura permitida)¶
aws apprunner describe-service --service-arn <ARN> --region us-east-1 \
--query 'Service.{status:Status, vars:SourceConfiguration.ImageRepository.ImageConfiguration.RuntimeEnvironmentVariables}'
7.4 Backup do cell DB¶
ssh ... 'kubectl -n temporal exec deploy/postgres -- pg_dump -U temporal cell' > cell-$(date +%F).sql
7.5 Troubleshooting¶
| Sintoma | Causa provável | Ação |
|---|---|---|
provisioner CrashLoop expected workflow.Context |
registrou função pura (não workflow) | registrar OnboardingWorkflow (já corrigido) |
buildkitd connection refused |
runc fora do PATH / sem host-net | PATH=…/k3s/data/current/bin buildkitd --oci-worker-net=host |
| cert público não fica Ready | HTTP-01 não alcança :80 / DNS não resolve | conferir A f2 (DNS-only) + firewall 80 + kubectl get challenges,orders |
| workflow falha em AllocateCell/ProvisionTenant | cell DB não wired (CELL_DATABASE_URL) |
conferir secret + Migrate no boot |
| trigger 401 | HMAC não bate | secret igual nos dois lados |
temporal --address localhost recusa |
frontend bind no pod IP | usar Service DNS / pod IP |
7.6 Desligar / pausar¶
kubectl -n octopus scale deploy/provisioner --replicas=0 (o enqueue do
customer-app degrada para log — checkout intacto).
8. Postura de segurança¶
- Firewall default-deny; API K8s (6443) + kubelet (10250) nunca públicos.
- SSH key-only + fail2ban + auto-updates.
- Trigger autenticado por HMAC constant-time; cert público Let's Encrypt.
- Tenant isolado por RLS (role
tenant_dataplaneNOBYPASSRLS). - Activities fail-closed — nunca fingem side-effect; money-path (RefundStripe) escala em vez de fingir.
- Secrets em K8s Secrets (cell) + App Runner config.
- ⚠️ Pendências de hardening: rotacionar o trigger secret (exposto em chat 2026-06-13);
mover
PROVISIONING_TRIGGER_SECRET/PROVISIONER_JWT_ED25519_SEEDpara um secret store; data-plane login role dedicado (hoje SET ROLE).
9. Custo¶
| Item | ~Custo/mo |
|---|---|
| Hetzner cx33 | $9-13 |
| Hetzner backups (opcional) | +$2 |
| AWS (incremental) | $0 (App Runner mesma config; CF DNS grátis) |
| Total incremental | ~$12-15/mo |
10. Limitações conhecidas / Wave-2¶
- SPOF: cell-0 é VPS único (sem HA). → 2º nó + Postgres gerenciado.
- Data-plane runtime: admissão CONNECT.Art/STUN-coord é por leitura de
slots(a tabela existe e isola); registro runtime ao vivo é follow-up Wave-1. - Redis namespace: lógico (prefixo
octopus:{cell}:{deployment}:*); ACL por-tenant é Wave-2. - Multi-cell / GeoIP routing:
AllocateCellretorna o cell único (Wave-1); routing por país é Wave-2 (ADR-0053). - Renewal/dunning: hoje pelo webhook Stripe (os workflows Temporal de renewal são scaffold).
- arm64 TBI: só amd64 publicado (F1).
Última verificação contra produção: 2026-06-13 — F2 LIVE, onboarding e2e provado (público + in-cluster). PRs #1364–#1368.