Skip to content

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 country en personas.yaml; el id de VLAN, el prefijo /24 público y el gateway se derivan vía lookup contra platform/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 chokepointauthorize() en dashboard/src/lib/license/gate.ts es 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/report de la cloud; la idempotencia es trabajo del controller (client_seq estable 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