# 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í.<br> ¿Hemos aumentado el estrés? Sí.<br> ¿Hemos aumentado las posibilidades de tener problemas? También.<br> ¿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<T>`, no nulos** *(responde: "¿qué es opcional y qué es obligatorio?")*
- No `null`, no "asume que está si no se especifica"
- `Option<T>`: 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<WorkflowTask>;
    async fn update_task(&self, id: &str, status: TaskStatus) -> StorageResult<()>;
    async fn list_tasks(&self, filter: TaskFilter) -> StorageResult<Vec<WorkflowTask>>;
    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<String> } }
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<String, TaskExecutionState>,
    pub checkpoints:    Vec<WorkflowCheckpoint>,  // 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<String, ProviderState>`
- 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<WorkflowExecutionState>,
    pub resources:       Vec<ResourceSnapshot>,
    pub provider_states: HashMap<String, ProviderState>,
}
```

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<String> },     // 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.*
