TLSStress.Art — Diseño de Alto Nivel del Sistema (HLD)¶
Audiencia: arquitectos, operadores, revisores de seguridad e inversores que necesitan la visión del sistema completo en un solo documento. Este HLD se sitúa por encima de la visión general de Arquitectura (tour de componentes) y de los Registros de Decisión de Arquitectura (justificación por decisión). Donde este documento resume, aquellos documentos son autoritativos en el detalle.
Profundizaciones complementarias: F2 — arquitectura del Cell-0 (Hetzner), postura de seguridad ZTP-prem, ADR-0053 (hyperscale por cells).
1. Propósito y alcance¶
TLSStress.Art es un banco de pruebas para medir el rendimiento de descifrado TLS en Next-Generation Firewalls (NGFWs) que actúan como Device Under Test (DUT). El objetivo primario es HTTP/3 (QUIC sobre UDP/443) — todo NGFW moderno reivindica inspección QUIC — con HTTP/2 (TCP/443) medido secundariamente para comparación.
El producto tiene dos caras:
| Cara | Qué es | Dónde corre |
|---|---|---|
| Plano de control SaaS | Signup, billing, ledger de tokens (TSU), auto-aprovisionamiento, enrollment central | AWS (App Runner + RDS) · Cloudflare (borde/DNS) · cell-0 Hetzner (K3s) |
| Appliance on-prem (TBI) | El banco en sí — agentes, personas, dashboard, observabilidad — instalado junto al NGFW del cliente | Datacenter/lab del cliente (K3s/RKE2 o Docker Desktop dev) |
El banco genera carga desde agentes (Playwright + k6) a través del NGFW, que descifra/inspecciona/re-firma TLS, y continúa hacia los webservers (las Personas). Comparando el tiempo de handshake y el throughput con descifrado ON vs BYPASS, el operador cuantifica el costo de inspección TLS del NGFW.
2. Contexto del sistema — actores¶
| Actor | Rol | Punto de contacto |
|---|---|---|
| Cliente | Compra un paquete en el SaaS, recibe un tenant aprovisionado + un appliance on-prem (TBI) para descargar | app.tlsstress.art (signup/pago), descarga del TBI (S3/F1) |
| Operador | Corre el banco contra un NGFW real. El Dashboard es la ÚNICA interfaz del operador — todo cambio de config pasa por la UI (nunca kubectl edit / curl admin/ / exec) |
Dashboard on-prem (Next.js) |
| NGFW DUT | El dispositivo bajo medición. Gateway L3 por defecto de las VLANs de test-plane; ejecuta el descifrado TLS | Firewall físico/virtual en trunks 802.1q |
flowchart LR
CUST(["Cliente"])
OPER(["Operador"])
subgraph SAAS["Plano de control SaaS (siempre-online)"]
APP["customer-app<br/>App Runner · Next.js<br/>signup · Stripe · mint TSU"]
CELL["cell-0 (Hetzner K3s)<br/>Provisioning Orchestrator<br/>saga Temporal · RLS de tenant"]
TBI[("Artefactos TBI<br/>S3 · img/qcow2/oci")]
end
subgraph PREM["Appliance on-prem (datacenter del cliente)"]
DASH["Dashboard (Next.js)<br/>la ÚNICA UI del operador"]
AGENTS["Agentes<br/>Playwright + k6"]
PERS["Personas<br/>100 webservers (Caddy)"]
OBS["Observabilidad<br/>Prometheus + Grafana"]
end
DUT{{"NGFW DUT<br/>descifra · inspecciona · re-firma TLS"}}
CUST -->|"signup / pago"| APP
APP -->|"applyTier (mint de quota)"| APP
APP -->|"enqueueOnboarding (HMAC)"| CELL
CUST -->|"descarga del appliance"| TBI
TBI -.->|"dd a USB / KVM / boot"| PREM
OPER -->|"login · configurar"| DASH
DASH --> AGENTS
DASH --> PERS
DASH --> OBS
AGENTS ==>|"leg TLS 1"| DUT
DUT ==>|"leg TLS 2"| PERS
AGENTS -.->|"eventos de spend TSU"| DASH
DASH -.->|"reporte de uso (horario)"| APP
classDef saas fill:#0F1A2E,stroke:#00D8FC,color:#FCFCFC,stroke-width:2px
classDef prem fill:#f0fdf4,stroke:#16a34a,stroke-width:1px
classDef dut fill:#fef9c3,stroke:#b45309,stroke-width:2px
class APP,CELL,TBI saas
class DASH,AGENTS,PERS,OBS prem
class DUT dut
3. Vista de contenedores (C4-ish)¶
El siguiente nivel se acerca a los contenedores desplegables y sus protocolos. El plano de control queda on-prem (Dashboard ↔ agentes por el bridge OOBI) mientras el plano de control de billing/aprovisionamiento vive en la cloud (customer-app ↔ cell-0). El único canal on-prem→cloud es el reporte horario de uso TSU.
graph TB
CUST(["Cliente<br/>navegador"])
OPER(["Operador<br/>navegador"])
subgraph CLOUD["Plano de control SaaS en la cloud"]
direction TB
APP["customer-app<br/>(App Runner, Next.js)"]
RDS[("RDS · ledger UTXO<br/>mint @ checkout")]
STRIPE{{"Stripe"}}
PROV["Provisioning Orchestrator<br/>(cell-0, Go + Temporal)"]
CELLDB[("Cell control DB<br/>tenants · slots (RLS)")]
end
subgraph ONPREM["Appliance on-prem"]
direction TB
DASH["Dashboard<br/>(Next.js · Drizzle)<br/>gate license authorize()"]
PG[("Postgres 16<br/>runs · agents · audit_log<br/>notas UTXO · idempotency")]
PWA["Agentes Playwright<br/>(1..80, HPA)"]
K6A["Agentes k6<br/>(1..200, HPA)"]
PERS["Personas (Caddy)<br/>100 / 20 países"]
VYOS["MÓDULO ISP/BGP<br/>(VyOS + FRR)"]
PROM["Prometheus + Grafana"]
BOOT["bootstrap-controller<br/>(drain de spend TSU)"]
end
DUT{{"NGFW DUT"}}
CUST -->|"HTTPS"| APP
STRIPE -->|"checkout.session.completed"| APP
APP -->|"applyTier mint"| RDS
APP -->|"HMAC POST /trigger"| PROV
PROV -->|"saga"| CELLDB
OPER -->|"cookie HMAC / Basic"| DASH
DASH -->|"SQL"| PG
DASH <-->|"Bearer + Idempotency-Key"| PWA
DASH <-->|"Bearer"| K6A
DASH -->|"spend de nota de licencia"| PG
PWA ==>|"leg TLS 1 · h2/h3"| DUT
K6A ==>|"leg TLS 1 · h2/h3"| DUT
DUT ==>|"leg TLS 2 (re-firmado)"| VYOS --> PERS
PROM -->|"scrape /api/metrics · SNMP"| DASH
BOOT -.->|"POST horario /api/usage/report"| APP
classDef cloud fill:#0F1A2E,stroke:#00D8FC,color:#FCFCFC,stroke-width:2px
classDef prem fill:#f0fdf4,stroke:#16a34a,stroke-width:1px
classDef dut fill:#fef9c3,stroke:#b45309,stroke-width:2px
class APP,RDS,STRIPE,PROV,CELLDB cloud
class DASH,PG,PWA,K6A,PERS,VYOS,PROM,BOOT prem
class DUT dut
4. Componentes principales y responsabilidades¶
4.1 Appliance on-prem (el banco)¶
| Componente | Código | Responsabilidad |
|---|---|---|
| Dashboard | dashboard/ |
Next.js 15 App Router. La única UI del operador. Auth por cookie HMAC, audit_log de toda mutación, stream SSE en vivo, /api/metrics Prometheus, el gate license authorize() (dashboard/src/lib/license/). |
| Agentes Playwright | agent/ |
Workers Chromium headless. BrowserContext nuevo por ciclo, captura protocolo / tcp_sockets_open / web-vitals. Circuit breaker por target, idempotency keys. HPA min=1, max=80. |
| Agentes k6 | k6-agent/ |
Wrapper Node.js del binario grafana/k6. Emite p50/p95/p99, error_rate, rps, data_received. readOnlyRootFilesystem, /tmp en memoria. HPA min=1, max=200. |
| Personas | personas/, webserver/ |
100 webservers Caddy en 20 países (5 cada uno). H2 + H3, TLS 1.2 mín, session tickets deshabilitados. Certs emitidos por persona-ca-issuer. Los endpoints del leg TLS 2. |
| MÓDULO ISP / BGP | bgp-router-peer/, ospf-router-peer/, vpn-remote/ |
VyOS + FRR. La ruta por defecto del NGFW; rutea hacia cada /24 de país. También stress de control-plane (inyección de LSA BGP/OSPF). |
| Postgres 16 | k8s/40-postgres.yaml |
runs, agents, k6_*, metrics_buckets, audit_log, idempotency, notas UTXO, envelopes de licencia. CronJob diario de pg_dump. |
| Observabilidad | observability/, grafana/ |
Prometheus + Grafana, SNMP exporter para NGFW/Nexus, node_exporter. Dashboards DUT (NGFW, Nexus, probe de decrypt-mode). |
| DUT API engine | dashboard/src/lib/dut-api/ |
Adapters de vendor (Cisco FTD/Nexus/UCS-CIMC, FortiGate). Captura config + identidad sanitizadas, AES-GCM en reposo, hashes SHA-256 de snapshot embebidos en el Test Run Report. |
| bootstrap-controller | pkg/bootstrap-controller/ |
Drena archivos de spend TSU y hace POST al /api/usage/report de la cloud, cada hora. |
4.2 Plano de control SaaS en la cloud¶
| Componente | Código | Responsabilidad |
|---|---|---|
| customer-app | pkg/octopus/customer-app/ |
Next.js en AWS App Runner. Signup, checkout Stripe, mintea la quota TSU en el ledger UTXO del RDS en el checkout (applyTier), y (fail-safe) encola el onboarding al cell-0. |
| Provisioning Orchestrator | pkg/octopus/provisioning-orchestrator/ |
Worker Go + Temporal en el cell-0. Corre el OnboardingWorkflow — saga de 12 actividades con compensación LIFO. Ver F2 HLD. |
| Cell control DB | internal/cell/migrations/ |
Database Postgres cell: tenants, slots, tenant_data con RLS Postgres para aislamiento por tenant (ADR-0053). |
| Admin console | pkg/octopus/admin-console/ |
Console interno de operador/billing (separado del Dashboard on-prem). |
| TBI builder | build/tbi/, pkg/tbi-agent/ |
Construye la imagen del appliance on-prem (mkosi → img/qcow2/oci). Embebe tbi-agent, k3s, gVisor, probes de hardware. |
5. Los dos legs TLS¶
El producto entero existe para medir el costo del descifrado entre dos legs TLS.
flowchart TB
A["Pod de agente<br/>(Playwright / k6)<br/>VLAN 20 / 30 — TRUST/INSIDE"]
DUT{{"NGFW DUT<br/>descifra · inspecciona · re-firma TLS"}}
VYOS["MÓDULO ISP (VyOS + FRR)<br/>VLAN 2001 — UNTRUST/OUTSIDE<br/>ruta por defecto 200.130.0.2"]
PERS["Pod Persona Caddy<br/>ej.: shop.us.persona.tlsstress.art<br/>= 198.32.10.10 · H2 + H3"]
A ==>|"leg TLS 1<br/>el agente confía en la CA del NGFW"| DUT
DUT ==>|"leg TLS 2<br/>el NGFW confía en persona-ca-issuer"| VYOS
VYOS -->|"una salida por LAN de país"| PERS
note1["Confianza leg 1: los agentes cargan NODE_EXTRA_CA_CERTS /<br/>SSL_CERT_FILE = CA del NGFW (ConfigMap 10-ngfw-ca)"]
note2["Confianza leg 2: el NGFW confía en el cert-manager<br/>persona-ca-issuer (platform/pki/)"]
note1 -.-> DUT
note2 -.-> VYOS
classDef agent fill:#dbeafe,stroke:#2563eb,stroke-width:1px
classDef dut fill:#fef9c3,stroke:#b45309,stroke-width:2px
classDef rtr fill:#fff7ed,stroke:#c2410c,stroke-width:1px
classDef pers fill:#f0fdf4,stroke:#16a34a,stroke-width:1px
classDef noted fill:#f5f5f7,stroke:#999,stroke-width:1px,color:#333
class A agent
class DUT dut
class VYOS rtr
class PERS pers
class note1,note2 noted
| Leg | De → Hacia | Ancla de confianza | Notas |
|---|---|---|---|
| Leg 1 | Agentes → NGFW | Los agentes confían en la CA del NGFW (k8s/dut/10-ngfw-ca.yaml, inyectada vía NODE_EXTRA_CA_CERTS / SSL_CERT_FILE) |
El NGFW intercepta y re-firma con su propia CA. Los agentes deben aceptarla para medir el camino inspeccionado. |
| Leg 2 | NGFW → Personas | El NGFW confía en el persona-ca-issuer (PKI cert-manager, platform/pki/) |
Las Personas presentan certs con IP SANs (IP público real) + DNS <persona>.<country>.persona.tlsstress.art. |
El NGFW solo ve el único edge link (VLAN 2001); el pod VyOS-ISP rutea hacia
cada /24 de país. El inventario de interfaces del NGFW es, por tanto, 8
interfaces (3 INSIDE + 4 OUTSIDE + 1 MGMT), no una por persona.
6. Planos de datos — eth0 / net1 / VLANs¶
Cada pod es dual-homed. El eth0 lleva control + observabilidad y nunca sale del host (bridge OOBI); el net1 es un macvlan que bypasea iptables/NetworkPolicy y lleva el tráfico de prueba real al trunk VLAN. Esta separación es lo que permite al banco dirigir tráfico a line-rate por un NGFW físico manteniendo la orquestación en un canal de control limpio.
flowchart TB
subgraph POD["Cada pod de plano de datos"]
ETH0(["eth0<br/>net por defecto K8s"])
NET1(["net1<br/>macvlan (Multus)"])
end
subgraph CTRL["Plano de control + observabilidad (eth0)"]
DASH["Dashboard :3000"]
PG["Postgres :5432"]
PROM["Prometheus :9090"]
end
subgraph DATA["Plano de datos (net1 → trunk 802.1q)"]
V20["VLAN 20 · 172.16.0.0/16<br/>Agentes Playwright (TRUST)"]
V30["VLAN 30 · 172.17.0.0/16<br/>Agentes k6 (TRUST)"]
V99["VLAN 99 · 10.254.254.0/24<br/>OOBI / SNMP mgmt"]
V40["VLAN 40 · DHCP<br/>Cloner ISP (SIN NGFW)"]
VCO["VLANs 101-120<br/>Personas — 1 VLAN/país<br/>/24 público real"]
end
DUT{{"NGFW DUT"}}
NET2["VyOS-ISP → LANs de país"]
INET(["Internet pública"])
ETH0 --> CTRL
NET1 --> V20 --> DUT
NET1 --> V30 --> DUT
NET1 --> V99
NET1 --> V40 --> INET
DUT --> NET2 --> VCO
classDef ctrl fill:#fce7f3,stroke:#db2777,stroke-width:1px
classDef data fill:#dbeafe,stroke:#2563eb,stroke-width:1px
classDef dut fill:#fef9c3,stroke:#b45309,stroke-width:2px
classDef ext fill:#f5f5f7,stroke:#000,stroke-width:1px
class DASH,PG,PROM ctrl
class V20,V30,V99,V40,VCO data
class DUT dut
class INET,NET2 ext
| Plano | Interfaz | Segmento(s) | Propósito |
|---|---|---|---|
| Control / mgmt | eth0 | net por defecto K8s | API del Dashboard, heartbeat, scrape Prometheus — nunca sale del host |
| Datos — Playwright | net1 | VLAN 20 172.16.0.0/16 |
Carga browser-driven a través del NGFW |
| Datos — k6 | net1 | VLAN 30 172.17.0.0/16 |
Carga TLS/HTTP sintética a través del NGFW |
| SNMP / OOBI | net1 | VLAN 99 10.254.254.0/24 (VXLAN VNI 254254) |
Polling SNMP + fabric de orquestación OOBI |
| Personas | net1 | VLANs 101-120 — una VLAN por país, /24 público real |
Endpoints del leg TLS 2 (detrás del VyOS-ISP) |
| Cloner ISP | net1 | VLAN 40 (DHCP) | Salida directa a internet del cloner de sitios — bypasea el NGFW |
Schema v4.3 / ADR-0007 (Realismo de Internet Pública): las personas llevan solo
countryenpersonas.yaml; el id de VLAN, el prefijo/24público y el gateway se derivan vía lookup contraplatform/network/public-ip-pool.yaml. El direccionamiento RFC1918 de persona (10.1.x.0/27) fue el estado v4.2 stale y no debe usarse.
7. El substrato de cell (ADR-0053)¶
El lado SaaS está construido cell-based desde el Wave-0 para escalar de decenas a cientos de miles de clientes sin migración pause-the-world.
| Concepto | Definición |
|---|---|
| Cell | Unidad de aislamiento de hasta 10 000 clientes con su propio stack OCTOPUS. El crash de una cell nunca afecta a otras. cell_id = <region>-<n> (ej.: hel1-0). |
| Sharding key | deployment_id (ULID / dpl_*) — la unidad de tráfico, que es lo que satura. |
| Plano de control | Servicios globales de todas las cells: Admin Console, Provisioning Orchestrator, token marketplace, PKI root, audit chain. |
| Plano de datos | Tráfico de cliente por-cell, cell-aislado (rendezvous CONNECT.Art + signaling STUN-coord). |
Wave-1 (live hoy) es una única cell — hel1-0 en un VPS Hetzner — pero todo
schema ya es cell-ready (columna cell_id en toda primary key), por lo que las
waves posteriores son incrementales, nunca un rewrite. El aislamiento por tenant
usa RLS Postgres en schema compartido (escala a 10k tenants/cell sin 10k
schemas): la tabla tenant_data tiene ENABLE/FORCE ROW LEVEL SECURITY con
policy USING (deployment_id = current_setting('octopus.tenant')). El plano de
control corre privilegiado; el plano de datos conecta con el role
tenant_dataplane (NOSUPERUSER NOBYPASSRLS), por lo que las lecturas
cross-tenant retornan cero filas.
Un slot (connect-art / stun-coord) es una entrada de admisión, no un
recurso de runtime escaso: los servicios de plano de datos leen slots para
admitir/rechazar un deployment_id. Ver F2 HLD §4.6
y ADR-0053.
flowchart LR
subgraph CP["Plano de control (global)"]
ADMIN["Admin Console"]
ORCH["Provisioning Orchestrator"]
PKI["PKI root / audit chain"]
end
subgraph CELL["Cell hel1-0 (Wave-1)"]
direction TB
CA["CONNECT.Art"]
SC["STUN-coord"]
DB[("tenants · slots · tenant_data<br/>(RLS, schema compartido)")]
end
ORCH -->|"AllocateCell · ProvisionTenant · ReserveSlot"| DB
CA -->|"lee slots → admite"| DB
SC -->|"lee slots → admite"| DB
ADMIN -.-> ORCH
classDef cp fill:#0F1A2E,stroke:#00D8FC,color:#FCFCFC,stroke-width:2px
classDef cell fill:#f0fdf4,stroke:#16a34a,stroke-width:1px
class ADMIN,ORCH,PKI cp
class CA,SC,DB cell
8. La economía de tokens (TSU)¶
El billing se mide en TSU (TLSStress Units). El flujo está deliberadamente dividido para que el saldo autoritativo viva en la cloud mientras el spend se origina on-prem.
sequenceDiagram
autonumber
participant C as Cliente
participant APP as customer-app (AWS)
participant RDS as ledger UTXO RDS
participant DASH as Dashboard on-prem
participant GATE as gate license authorize()
participant PG as Postgres on-prem (notas UTXO)
participant BOOT as bootstrap-controller
participant USAGE as /api/usage/report (cloud)
C->>APP: checkout (Stripe)
APP->>RDS: applyTier — MINT de quota (autoritativo)
Note over DASH,GATE: toda ejecución de test plan pasa por el gate
DASH->>GATE: authorize(testPlanId, estimatedTokens, features)
GATE->>PG: spendNotes() — quema seriales de nota UTXO
GATE->>PG: sealedAppend() — registro de auditoría WORM
GATE-->>DASH: recibo {sealedHash, burnedSerials, balanceAfter}
DASH->>DASH: run ejecuta — los agentes emiten Tick(TSU) por run finalizado
BOOT->>BOOT: drena /var/lib/tlsstress/spend/*.jsonl (horario)
BOOT->>USAGE: POST eventos de uso (client_seq estable → dedup)
Propiedades clave (fieles a pkg/metering/README.md, dashboard/src/lib/license/):
- Mint autoritativo en el checkout — el customer-app mintea la quota en el
ledger UTXO del RDS (los tokens son notas tamper-evident, no un saldo
mutable; Patent #19, sin columna
balance, sin race de update). - Spend on-prem — cada módulo emite un
Tick(TSU)por unidad de trabajo finalizada en/var/lib/tlsstress/spend/<module>.jsonl. Un meter roto no puede tumbar el módulo (hace fallback a stderr). - Gate de licencia como chokepoint —
authorize()endashboard/src/lib/license/gate.tses el único chokepoint por el que pasa todo test run: gasta notas UTXO, hace append en el audit hash-chain sellado y retorna un recibo firmado. Wave-1 es always-allow (envelope de desarrollador); el enforcement (verificación de firma, gating de features, caps de concurrencia, expiry+grace) se activa bajo la misma firma en v5.3+. - Reconciliación horaria — el bootstrap-controller drena los archivos de spend
y hace POST al
/api/usage/reportde la cloud; la idempotencia es trabajo del controller (client_seqestable por evento → la cloud rechaza el duplicado en el retry).
9. Postura de seguridad (alto nivel)¶
La postura completa de Zero-Trust-on-Premises (ZTP-prem) de 12 capas — que defiende el banco contra un operador insider con
kubectl+ root dentro de la propia org del cliente — está en SECURITY_ZTP_PREM.md. Esta sección es la baseline operacional que aplica independientemente del estado de layer-flip del ZTP-prem.
| Capa | Control |
|---|---|
| Containers | Non-root, capabilities Linux dropeadas, seccomp RuntimeDefault, root filesystem read-only donde sea posible. |
| Red | NetworkPolicy de egress default-deny; RFC1918 + link-local bloqueados de la flota de agentes. Personas detrás del VyOS-ISP; movimiento lateral OOBI bloqueado (solo la VIP del Infra Stack 10.254.254.100 puede iniciar hacia IPs OOBI de MÓDULO). |
| Auth | Cookie HttpOnly firmada por HMAC en el Dashboard (comparación en tiempo constante, rate limit por IP); Authorization: Basic legado para callers programáticos. El trigger de la cloud es HMAC-SHA256 con verify en tiempo constante. |
| Supply chain | gitleaks + trivy por PR, CodeQL semanal; imágenes firmadas Cosign-keyless vía GitHub OIDC; SBOM SPDX adjunto por release tag. |
| Gate de pre-flight | Catálogo de 5 checks antes de cada run (conflicto de subnet, alcance NGFW + decrypt mode, frescura de la PKI de persona, skew NTP, auth de la DUT API). El bypass se loguea en audit_log + se imprime en la portada del report. |
| Datos en reposo | Credenciales de la DUT API cifradas con AES-GCM; snapshots sanitizados con hash SHA-256 en el Test Run Report (Anexo B/C/D). |
| Aislamiento de tenant (cloud) | RLS Postgres con el role tenant_dataplane (NOBYPASSRLS); lectura cross-tenant = 0 filas (probado live). |
| Auditabilidad | audit_log registra toda mutación de config (actor, IP de origen, JSON before/after); audit hash-chain sellado del ZTP-prem (WORM) registra toda decisión de licencia/admission/egress. |
10. Topología de deployment (ADR-0011)¶
La topología se configura vía platform/topology.yaml en tres ejes
independientes, por lo que un lab single-node y un fabric multi-node de prod
comparten una única base de código.
| Eje | Valores | Controla |
|---|---|---|
deployment_nodes |
single · dual · tri · multi |
Número de hosts UCS + distribución de roles |
l2_fabric |
nexus · none |
Switch L2 externo (o su ausencia) |
dut_type |
cisco-ftd · cisco-secure-router |
Vendor del DUT — gatea scripts de apply/verify |
Single-node sin switch externo es first-class (l2_fabric: none): NICs del
UCS cableadas directo al NGFW, tuning Nexus omitido, pero la atestación BPDU de
Linux-bridge sigue corriendo. El plano OOBI (eth0) es mandatorio en todo host en
todo modo. Los overlays Kustomize (overlays/dual-node|tri-node|multi-node/)
fijan cada tier de workload a su host dedicado vía patches de nodeSelector.
11. Referencias cruzadas¶
| Tema | Documento |
|---|---|
| Tour de componentes, diagramas de secuencia, topología DUT | docs/ARCHITECTURE.md |
| Profundización F2 auto-aprovisionamiento + cell-0 | docs/F2_HETZNER_CELL0_ARCHITECTURE.md |
| Seguridad ZTP-prem de 12 capas | docs/SECURITY_ZTP_PREM.md |
| Justificación de hyperscale por cells | ADR-0053 |
| Índice de registros de decisión | ADR/README.md |
| Convenciones y reglas del proyecto | CLAUDE.md |