# Talk Structure: Why I Needed Rust > Orientado a: `abstract_en.md` > Fuente: `2026-02-17-notas_voz.md` --- ## Sistema de Medidores *(elemento visual persistente)* Tres barras que aparecen en esquina de cada slide de transición. Escala 0–5. Se actualizan en cada etapa clave. ``` 🛡 Confianza ●●●●● = máxima certeza sobre el sistema 😴 Descanso ●●●●● = puedes dormir tranquilo 🔥 Estrés ●●●●● = alarma permanente, pesadillas ``` **Estado inicial (apertura)** ``` 🛡 ●●●○○ 😴 ●●●○○ 🔥 ●○○○○ ``` *Se presenta como "¿Reconoces estos números? Sigamos..."* **Progresión a lo largo de la charla:** | Momento | 🛡 Confianza | 😴 Descanso | 🔥 Estrés | |---|:---:|:---:|:---:| | Etapa 1 — Local | ●●●●○ | ●●●●○ | ●○○○○ | | Etapa 2 — Redes | ●●●○○ | ●●●○○ | ●●○○○ | | Etapa 3 — Cloud/CI-CD | ●●○○○ | ●○○○○ | ●●●●○ | | YAML hell / peak pain | ●○○○○ | ○○○○○ | ●●●●● | | Rust — compilador | ●●●○○ | ●●○○○ | ●●●○○ | | Rust — tipos + traits | ●●●●○ | ●●●●○ | ●●○○○ | | Cierre | ●●●●● | ●●●●● | ●○○○○ | > El título de la charla ES el último estado del medidor de sueño. --- ## 0. Hook de apertura **Imagen concreta**: definir un host, un nodo, un puerto. - Eso es todo. Queremos que algo corra en algún sitio y que algo pueda hablar con él. - ¿Cómo de difícil puede ser? **Promesa**: vamos a ver por qué eso, durante 30 años, nos ha dado pesadillas. Y por qué Rust cambió eso. *→ Mostrar medidores en interrogante o en estado inicial neutro* --- ## 1. Evolución de los desafíos en infraestructura (2013-2025) *Abstract: "The evolution of infrastructure challenges (2013-2025)"* ### Etapa 1 — Local (finales 80s / primeros 90s) - Terminales tontos, desarrollo local - Ciclos de despliegue largos, baja urgencia - Un solo estado, fácil de observar y controlar - IaC: scripts procedurales, lógica oculta en comandos o en la aplicación > *Medidores fin Etapa 1:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●○○○○` > *"Tienes un servidor. Sabes lo que tiene. Puedes dormir."* ### Etapa 2 — Conectividad en red / Internet - Los sistemas están cada vez más lejos — acceso remoto, redes distantes - Seguridad empieza a importar; procesos críticos, coste de caída sube - Más agentes participando (dev, ops, seguridad) — hay que armonizar - Armonizar: instalación de paquetes, configuración de recursos, actualizaciones en paralelo - IaC: procesos automatizables y reproducibles, primeros intentos declarativos, config-driven model > *Medidores fin Etapa 2:* `🛡 ●●●○○ 😴 ●●●○○ 🔥 ●●○○○` > *"Más piezas. Más personas. Empieza a ponerse interesante."* ### Etapa 3 — Contenedores / Cloud / CI-CD - Monolítico → distribuido, 24×7×365, alta disponibilidad - Cloud, híbrido, multi-cloud, on-prem — todo a la vez - Rollback y rollforward: como transacciones de BD, pero en infraestructura - Escalado horizontal **y** vertical, también **desescalado** — en ambas direcciones - CI/CD continuo, ciclos cortos — nuevas funcionalidades, nuevos despliegues, permanente - IaC: Helm, Ansible, Terraform — más herramientas, más complejidad - Múltiples versiones de "lo que creemos que es válido" — la fuente de verdad se fragmenta - **Resultado**: estados de alarma como norma, no como excepción > *Medidores fin Etapa 3:* `🛡 ●●○○○ 😴 ●○○○○ 🔥 ●●●●○` > *"¿Hemos aumentado la productividad? Sí.
¿Hemos aumentado el estrés? Sí.
¿Hemos aumentado las posibilidades de tener problemas? También.
¿Tenemos más control y seguridad? No."* --- ## 2. Por qué el IaC tradicional falla a escala *Abstract: "Why traditional approaches to IaC fall short at scale"* ### La analogía del restaurante *(hilo conductor de toda la charla)* Un restaurante tiene tres actores: **comensal** (mesa), **camarero** (servicio), **cocina**. | Restaurante | Infraestructura | |---|---| | Comensal declara lo que quiere | Config declarativa (YAML, HCL, Helm) | | Camarero valida y transmite | Orchestrator (K8s, Ansible, Terraform) | | Cocina ejecuta y entrega | Runtime / provisioning | | El plato llega — o no | Deployment exitoso — o no | **Lo que hace que funcione** — o no: - El comensal declara, no implementa: no dice *cómo* cocinar, dice *qué* quiere - El camarero debe saber qué es posible y validar *antes* de ir a cocina - "Quiero X" → camarero va a cocina → "X no hay, ¿por qué está en la carta?" → vuelve a mesa - Equivalente: configuré un host con el puerto 8443 → ese puerto no está permitido → hay que reconfigurar desde cero **La verdad que muta — y el problema de estado** - La "verdad" no es estática. Muta en cada paso de la cadena: - Comensal: su petición oral - Camarero: la nota que toma - Cocina: las marcas sobre lo que ya está hecho o no - Caja: el ticket de pago - Cada actor ve una *parte* de la verdad, relevante para él en ese momento - Como en un sistema neuronal: hay eventos que no activan ciertas neuronas porque no son relevantes para ellas - **El camarero conoce al cliente habitual** ("siempre sin sal") — la cocina, no. ¿Está ese contexto explícito en el pedido, o asumido? - Si el camarero cambia, o si interviene otra estación de cocina → el estado implícito se pierde **El coste del fallo depende de dónde ocurre** - Fallo en mesa al pedir (pedido imposible): barato — se corrige antes de llegar a cocina - Fallo en cocina al elaborar (ingrediente no disponible): medio — hay que volver a mesa y renegociar - Fallo al entregar (plato se cae): caro — hay que rehacer desde cero - Fallo cuando llega el plato y no es lo que se pidió: muy caro — experiencia destruida + tiempo perdido - **Fail early = fail cheap. Fail in production = nightmare.** **La renegociación: "no tengo champiñón"** - Un actor en la cadena descubre que no puede cumplir parte del pedido - Debe volver atrás y validar con el comensal: "¿te pongo verduras?" - Esto requiere que el cambio sea explícito, trazado, y re-autorizado — no silencioso - Equivalente en infra: configuration drift, estado divergente sin notificación --- ### El ciclo de la config: cómo llegamos hasta aquí 1. **Hardcoded** — dependencias, rutas, puertos dentro del binario. Control total, cero flexibilidad 2. **Config externa** (JSON) — funciona bien entre máquinas, ilegible para humanos a escala 3. **YAML / TOML** — más legible, pero sintaxis frágil: indentación, tipos implícitos, errores silenciosos 4. **YAML + Serde** — cargamos la config con Serde, que valida la *estructura*: - ¿Existe el campo? ¿Es del tipo correcto? - ¿Aceptamos "elefante" como animal de compañía? Depende del tipo. Pero si el tipo es `String`... sí. - **Serde valida forma. No valida significado.** 5. **Helm / Jinja templates** — escribimos YAML a partir de variables (en YAML o JSON) - El template genera el YAML final que consume la aplicación - ¿Valida el contenido del YAML generado? **No. En absoluto.** - Como usar un LLM con un markdown de referencia: el formato está, pero ¿el contenido es correcto? Eso no lo garantiza nadie. 6. **Resultado**: CI/CD continuo + config sin validación semántica = **esperanza continua** > *"Lo que hacemos es escribir lo que queremos, como una carta a los Reyes Magos. Y cruzamos los dedos."* --- ``` ┌─────────────────────────────────────────────┐ │ SLIDE STANDALONE — Esperanza continua │ │ │ │ CI/CD continuo. │ │ Sin validación semántica. │ │ │ │ Esperanza continua. │ │ │ │ (cruzamos los dedos en producción) │ └─────────────────────────────────────────────┘ ``` > *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●` > *Tono: irónico, de reconocimiento. La audiencia asiente.* --- ### Las tres preguntas sin respuesta **Pregunta 1 — ¿Por qué esperamos a que se rompa?** - "In my machine it works" — en producción, no lo sé - Fail late = coste máximo - Queremos: fail fast, fail cheap — cuanto antes, menor el coste **Pregunta 2 — ¿Tenemos claro lo que queremos?** - ¿La declaración es suficiente y consistente con lo que es posible? - ¿Respeta los límites del sistema? ¿Cuáles son esos límites — estáticos o dinámicos? - ¿Cuál es la fuente de verdad? ¿Y cuándo y cómo muta? **Pregunta 3 — ¿Podemos garantizar determinismo?** - ¿Tenemos herramientas que usen nuestras definiciones para operar automáticamente en el ciclo de vida? - ¿Podemos tener certeza de que obtendremos lo que formulamos — no de forma aleatoria? - No "en mi máquina funciona". Siempre. En cualquier máquina. En cualquier momento. > *En realidad no estamos inventando nada. Todo ya existe. El problema es si lo estamos gestionando adecuadamente.* --- ``` ┌─────────────────────────────────────────────┐ │ SLIDE STANDALONE — Pesadilla continua │ │ │ │ Sistemas que no sabemos cómo controlar. │ │ Esperamos que funcionen. │ │ Cuando no funcionan, los arreglamos. │ │ │ │ Pesadilla continua. │ │ │ │ (el estado de alarma es lo normal) │ └─────────────────────────────────────────────┘ ``` > *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●` > *Tono: oscuro, sin ironía. El suelo emocional de la charla. Pausa aquí.* --- ## 3. Type safety y memory safety como fiabilidad en producción *Abstract: "Type safety and memory safety as production reliability"* ### El puente: de YAML+Serde a tipos Rust Serde carga config estructuralmente válida. Pero "elefante" como valor de `animal_companía: String` compila. La respuesta de Rust: **no uses `String`. Usa un tipo.** ```rust enum Animal { Perro, Gato, Conejo } // "elefante" no compila ``` Eso es lo que cambia. No el formato de la config. El modelo de qué puede contener. --- ### Lo que Rust introduce — respuesta a las tres preguntas **Tipado estático estricto** *(responde: "¿tenemos claro lo que queremos?")* - Un número no es un string. Un puerto no es cualquier entero. - Los tipos son la declaración de intenciones — sin ambigüedad, sin equívocos - Las conversiones (cast) son explícitas, no implícitas - Tipos primarios ricos + structs, enums, tuplas, newtypes **Inmutabilidad por defecto** *(responde: "¿qué puede cambiar?")* - "Quiero tomate. Puedes inventarte lo que quieras, pero sin tomate no es el plato que pedí." - `let` es inmutable. `let mut` es explícito y deliberado. - Los invariantes son invariantes — no silenciosamente mutados en runtime **`Option`, no nulos** *(responde: "¿qué es opcional y qué es obligatorio?")* - No `null`, no "asume que está si no se especifica" - `Option`: o está (`Some`) o no está (`None`) — y ambos casos se manejan explícitamente - Restaurante: "champiñón opcional. Si no está, lo sé. Lo decido. Lo gestiono." - El camarero no puede ignorar que no hay champiñón — el compilador no le deja **Enums como dominio cerrado** *(responde: "¿cuáles son los valores válidos?")* - Un cloud provider es `enum Provider { AWS, GCP, Azure, OnPrem }` — no un `String` libre - Un puerto es un `u16` con rango validado — no cualquier entero - Las restricciones están en el tipo, no en comentarios, no en documentación, no en la memoria del equipo **Traits como contratos entre actores** *(responde: "¿pueden los actores de la cadena cambiar?")* - Composición en vez de herencia rígida - Cada "actor" de la cadena (AWS provider, K8s, on-prem) implementa el mismo trait - La cadena funciona con cualquier implementación que cumpla el contrato - Como el restaurante: el camarero puede cambiar — si conoce el protocolo, el proceso es el mismo **El compilador como el camarero que valida** *(responde: "¿cuándo detectamos el fallo?")* - Valida **antes** de construir el binario — no en runtime, no en producción - No cuando lleva horas ejecutándose. No cuando se llama a una función que nadie tocaba hace meses. - Fail early. Fail cheap. En compilación, no en el 2am del domingo. - Binarios predecibles: comportamiento de memoria, recursos y flujos deterministas > *"El compilador es el camarero que valida el pedido antes de que llegue a cocina. Antes de que el cliente espere. Antes de que el ingrediente no esté."* > *Medidores — compilador activo:* `🛡 ●●●○○ 😴 ●●○○○ 🔥 ●●●○○` > *Medidores — tipos + traits + options:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●●○○○` > *Medidores — Rust completo en producción:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○` --- ``` ┌─────────────────────────────────────────────┐ │ SLIDE STANDALONE — Certeza continua │ │ │ │ El compilador valida antes del binario. │ │ Los tipos definen lo que es posible. │ │ El contrato se cumple — o no compila. │ │ │ │ Certeza continua. │ │ │ │ (para continuar durmiendo bien) │ └─────────────────────────────────────────────┘ ``` > *Medidores:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○` > *Tono: resolución. El arco cierra. El título de la charla, demostrado.* --- ## 4. Orquestación segura multi-cloud y on-prem *Abstract: "Building safe orchestration across multi-cloud and on-prem environments"* ### De la teoría al sistema real Todo lo anterior no es hipotético. Es la arquitectura de **Provisioning** — un sistema de orquestación de infraestructura en producción, escrito en Rust, que gestiona despliegues en AWS, UpCloud y on-prem desde una sola base de código. La pregunta es: ¿cómo se traduce "tipos y traits" a un sistema que maneja multi-cloud real? --- ### Nickel como source of truth tipado (ADR-003) YAML fue rechazado explícitamente. TOML fue rechazado. La razón: sin type safety. Nickel — un lenguaje de configuración funcional con sistema de tipos — reemplaza YAML: ```nickel { infrastructure = { compute | { region | String, count | Number & (> 0), auto_scaling | { min | Number & (> 0), max | Number & (>= min), -- el compilador verifica esto } } } } ``` - Validación en tiempo de compilación de la config — no en runtime, no en producción - Merging jerárquico tipado: defaults → workspace → profile → environment → runtime - Resultado (ADR-003): **zero configuration type errors en producción** - Alternativas rechazadas explícitamente: TOML (no type safety), KCL, YAML+validación híbrida > *"Serde valida forma. Nickel valida significado. El compilador valida antes del despliegue."* --- ### Traits como contratos de provider (el restaurante, resuelto) El problema del restaurante: la cocina puede cambiar. AWS no es UpCloud no es bare metal. La solución Rust: todos implementan el mismo trait. ```rust // El trait es el menú. Los providers son las cocinas. #[async_trait] pub trait TaskStorage: Send + Sync { async fn create_task(&self, task: WorkflowTask) -> StorageResult; async fn update_task(&self, id: &str, status: TaskStatus) -> StorageResult<()>; async fn list_tasks(&self, filter: TaskFilter) -> StorageResult>; async fn audit_operation(&self, op: AuditEntry) -> StorageResult<()>; } // Implementaciones: FilesystemStorage, SurrealDbStorage, MemoryStorage // El orchestrator no sabe cuál usa. El compilador garantiza que todas cumplen. ``` ```rust // Dominios cerrados — no strings libres enum RollbackStrategy { ConfigDriven, Conservative, Aggressive, Custom { operations: Vec } } enum DependencyType { Hard, Soft, Optional } enum TaskStatus { Pending, Running, Completed, Failed, Cancelled } enum ProviderType { UpCloud, AWS, Local } ``` Cuando se añade un nuevo provider: implementa el trait o no compila. No hay forma de olvidarse de un caso. --- ### Orquestación con grafo de dependencias El orchestrator construye un DAG (grafo acíclico dirigido) de tareas con detección de ciclos en tiempo de compilación del workflow. ```rust pub struct WorkflowConfig { pub max_parallel_tasks: usize, pub task_timeout_seconds: u64, pub fail_fast: bool, // falla rápido, falla barato pub checkpoint_interval_seconds: u64, } ``` Instalación de Kubernetes como ejemplo — el orchestrator resuelve: ``` containerd (Hard) → etcd (Hard) → kubernetes → cilium (requires kubernetes) → rook-ceph ``` No se puede instalar kubernetes si containerd falla. El tipo `DependencyType::Hard` lo garantiza. El compilador detecta dependencias circulares. No el operador de guardia a las 3am. --- ### El estado explícito elimina el drift El problema de configuration drift: el sistema real diverge silenciosamente del estado deseado. Rust obliga a que toda mutación sea explícita (`let mut`). El sistema de estado del orchestrator hace lo mismo a nivel de infraestructura: ```rust pub struct WorkflowExecutionState { pub status: WorkflowStatus, pub task_states: HashMap, pub checkpoints: Vec, // qué se hizo y cuándo pub statistics: WorkflowStatistics, } ``` - Checkpoints cada 5 minutos (configurable) - Hasta 50 checkpoints retenidos - Cada estado de provider capturado en `HashMap` - Sin estado implícito. Sin "el camarero recuerda que el cliente no quiere sal". Está en el pedido. > *Medidores — sección 4:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○` --- ## 5. Aplicaciones reales *Abstract: "Real applications: Kubernetes, blockchain validators, disaster recovery"* ### Kubernetes — despliegue con tipos El orchestrator se despliega en Kubernetes. 3 réplicas, rolling update, anti-affinity entre nodos. Pero lo relevante no es que *corre* en Kubernetes — es cómo se *gestiona* Kubernetes como target. El orchestrator provisiona los componentes del cluster como workflow tipado: ``` containerd → etcd → kubernetes control plane → kubelet (workers) → CoreDNS → Cilium (CNI, requires kubernetes) → Rook-Ceph (storage, requires kubernetes + Cilium) ``` Cada componente es un tipo. Cada dependencia es un `DependencyType`. El compiler detecta si intentas instalar Cilium sin Kubernetes. No el CI a las 2am. Los health probes del orchestrator en K8s — `/health` cada 10 segundos — no son decoración. Son parte del contrato: el sistema sabe cuándo está sano. Lo sabe antes de que falle el cliente. --- ### Blockchain validators — disponibilidad como tipo Los validators de blockchain (Polkadot, Ethereum) tienen un requisito brutal: uptime o slashing. Un validator que falla pierde fondos. No hay margen para "en mi máquina funciona". Lo que Rust + este sistema aporta: **Secretos con post-quantum cryptography (ADR-006)** - CRYSTALS-Kyber (KEM) + Falcon (signatures) + AES-256-GCM (hybrid) - Las claves del validator no pueden ser comprometidas por computación cuántica futura - La rotación de claves es automática, trazada, con audit log **SLOs con error budgets reales (ADR-009)** ``` Tier 1 — Critical: 99.99% uptime = 52.6 minutos de downtime/año ``` Esto no es un número de marketing. Es un Prometheus rule que dispara alertas y bloquea deploys cuando el burn rate supera el presupuesto. **Configuración determinista** - Los parámetros del validator son tipos, no strings: no puede entrar un valor de `bond_amount` que no sea un `u128` validado - La config del validator es reproducible bit-a-bit en cualquier nodo — mismo Nickel schema, mismo resultado --- ### Disaster recovery — rollback como tipo, no como procedimiento El problema clásico de DR: el runbook existe, pero no se ejecutó correctamente, o el estado en DR no coincide con el de producción. **Checkpoints como snapshots de estado completo** ```rust pub struct Checkpoint { pub workflow_state: Option, pub resources: Vec, pub provider_states: HashMap, } ``` No es "vuelve al commit anterior". Es el estado completo del sistema: qué estaba corriendo, en qué provider, con qué configuración, en qué momento. **Rollback como estrategia tipada** ```rust enum RollbackStrategy { ConfigDriven, // lo que diga la config Conservative, // preserva todo salvo lo marcado Aggressive, // revierte todo Custom { operations: Vec }, // playbook explícito } ``` No hay forma de hacer rollback sin elegir una estrategia. El compilador no te deja ignorar el caso. **Self-healing automatizado (ADR-010)** ``` ┌─────────────────────────────────────────────┐ │ SLIDE STANDALONE — MTTR │ │ │ │ Sin tipos. Sin compilador. Sin estado. │ │ │ │ MTTR > 30 minutos. │ │ │ │ (a las ??, apagar el fuego) Alarma pesadilla│ │ ──────────────────────────────── │ │ │ │ Rust. Tipos. Estado explícito. │ │ Automated response. │ │ │ │ MTTR < 5 minutos. │ │ │ │ (a las 3am, sin ti) Descanso en paz mental │ └─────────────────────────────────────────────┘ ``` > *Medidores:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○` > *Tono: cifras reales, no promesas. Pausa después de "sin ti".* El `RemediationEngine` ejecuta playbooks tipados: `ScaleService`, `FailoverService`, `RestartService`, `ClearCache`. Si el remedio falla 3 veces, escala a humano. No itera indefinidamente. **Multi-backend backup tipado** - restic, borg, tar, rsync — todos como variantes de un enum - El backup de producción y el restore en DR usan el mismo tipo, el mismo schema - "Funciona en prod pero no en DR" no puede ocurrir si el estado es el mismo tipo > *"No estamos cruzando los dedos. Tenemos el estado. Tenemos el tipo. Tenemos el rollback. Dormimos."* > *Medidores — sección 5:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○` --- ## Cierre - No hemos inventado nada nuevo. Los problemas siempre han existido. - Rust los reúne y los resuelve: tipos, traits, compiler, memory safety. - El resultado: infraestructura predecible. Deployments sin pesadillas. - **Duermes bien.** > *Medidores — cierre:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○` > > *Slide final: los tres medidores en verde. Título de la charla. Fin.*