<!-- The markdown twin of https://kleidos.dev/es/architecture/, converted from that page at build time. The page is the original; this is the same words with the markup taken off. The site was last changed on 2026-10-25. -->

# Arquitectura

Con un solo árbol de fuentes se compila el firmware de nueve placas de dos familias de chip. Lo que lo hace posible no es ingenio, sino una forma que se comprueba en cada commit: un módulo puede usar los que están por debajo y ninguno por encima, y la compilación falla cuando eso deja de ser verdad.

Igual que la página de seguridad, esta describe un repositorio que todavía no puedes abrir. La comprobación se ejecuta en cada commit; que puedas ejecutarla tú es un paso de la hoja de ruta.

En esta página

1. [Capas](https://kleidos.dev/#layers)

2. [El grafo](https://kleidos.dev/#graph)

3. [Dos núcleos](https://kleidos.dev/#cores)

4. [Un solo árbol](https://kleidos.dev/#onetree)

## El orden que nada puede subir

Veintidós módulos en un único orden, el consumidor primero. Un módulo incluye sus propias cabeceras y las de los módulos que tiene debajo; un include que apunte hacia arriba no pasa la comprobación, en el gancho del commit y otra vez en integración continua.

Lee una fila de izquierda a derecha: sus puntos son los módulos que incluye el de esa fila, por su número. Los 117 includes entre módulos quedan a la derecha de la diagonal, cada uno por debajo del módulo que lo incluye en el orden. El triángulo vacío de su izquierda es donde iría un include que subiera, y la compilación se niega a dibujar esta figura si aparece alguno.

El remedio para un include hacia arriba nunca es una excepción. Es bajar la cabecera, o invertir la llamada con una interfaz que el módulo de abajo declare, que es como el panel de administración llega a la aplicación en marcha sin que la aplicación sepa que existe un panel. Hay un mecanismo para fijar excepciones, una por una. Está vacío, y lo importante es precisamente que esté vacío: en el firmware no hay dos módulos que dependan el uno del otro.

Los includes se escriben desde la raíz del árbol de fuentes, así que una ruta relativa que se salga de su propio directorio no pasa la misma comprobación. Dentro de un módulo la comprobación va más allá y pone vallas entre unos grupos de ficheros y otros, y rechaza un ciclo entre cabeceras. El dibujo de la sección siguiente se genera con esas mismas líneas de include, y la comprobación falla mientras la copia que se guarda en el repositorio esté desfasada.

## El grafo

El grafo de módulos se genera a partir de los propios includes en vez de dibujarse a mano, y eso es lo que evita que el diagrama y el código que describe se contradigan: la herramienta que lo dibuja es la que impone el orden.

### Montaje y control

Los archivos que nombran cada pieza de una placa y la gobiernan. Están arriba porque alcanzan todo lo de abajo; nada los alcanza a ellos.

1. **El cableado de la placa** `boards/`Un archivo por dispositivo: qué pantalla, qué controlador de energía, qué botones tiene de verdad esta placa. usa`ui/` `diag/` `hal/` `config/`

2. **El compañero de banco** `tools/`Una segunda placa que hace de ordenador, para probar la radio y la página de administración contra algo real. usa`platform/` No en todas las compilaciones

3. **La máquina de estados** `main.cpp`Arranque y un solo bucle: dormido, bloqueado, abierto, escribiendo, administración, y cada paso entre ellos. usa`debug/` `states/` `web/` `ui/` `reset/` `net/` `ble/` `diag/` `vault/` `totp/` `i18n/` `crypto/` `hal/` `drivers/` `time/` `platform/` `ota/` `config/`

4. **La consola del desarrollador** `debug/`Una línea de comandos por serie que conduce cada pantalla. No se compila en una versión de uso, y cada compilación se comprueba. usa`states/` `web/` `ui/` `reset/` `net/` `ble/` `diag/` `vault/` `totp/` `i18n/` `crypto/` `hal/` `drivers/` `time/` `platform/` `config/` No en todas las compilaciones Probado en un PC

### Pantallas y superficies

Todo lo que una persona mira o donde escribe, en el dispositivo o en un navegador.

1. **Las pantallas** `states/`Qué muestra cada estado y qué hace una pulsación ahí: el dial del PIN, la bóveda, el árbol de ajustes, el primer arranque. usa`web/` `ui/` `reset/` `net/` `ble/` `diag/` `vault/` `totp/` `i18n/` `crypto/` `hal/` `drivers/` `time/` `platform/` `config/` Probado en un PC

2. **La página de administración** `web/`Una página que el dispositivo sirve desde su propio Wi-Fi, para editar en lote y actualizar. Apagada hasta que la enciendes, y nunca junto a la radio. usa`ui/` `reset/` `net/` `ble/` `diag/` `vault/` `totp/` `i18n/` `crypto/` `hal/` `drivers/` `time/` `platform/` `config/` Probado en un PC

3. **La biblioteca de dibujo** `ui/`Cada píxel: listas, diálogos, iconos, marcas. Un juego de piezas que encaja en nueve pantallas, del cuadrado de tinta a la táctil en color. usa`ble/` `vault/` `brands/` `totp/` `i18n/` `crypto/` `hal/` `time/` `platform/` `config/` Probado en un PC

### Servicios del dispositivo

Lo que hace el dispositivo una vez abierto: escribir, conectar, borrar, medirse.

1. **El borrado** `reset/`Un único sitio que borra todo (bóveda, emparejamientos, ajustes) para que cada botón y cada página que borra haga exactamente lo mismo. usa`net/` `ble/` `vault/` `platform/` Probado en un PC

2. **Los recados de red** `net/`Unirse a un Wi-Fi lo justo para poner el reloj, y recordar el nombre con el que responde la página de administración. usa`ble/` `vault/` `crypto/` `time/` `platform/` Probado en un PC

3. **El teclado en que se convierte** `ble/`El teclado Bluetooth que ve tu ordenador. Solo envía pulsaciones: no hay camino por el que pudiera leer lo que escribes. usa`time/` `platform/` Probado en un PC

4. **Los instrumentos** `diag/`Lo que el dispositivo mide de sí mismo: batería, memoria libre, cuánto tardó en dibujarse un fotograma. usa`hal/` `drivers/` `time/` `platform/` Probado en un PC

### Datos y secretos

Lo que el dispositivo guarda y lo que lo cierra. Por debajo de cada pantalla y cada radio: la bóveda no depende de lo que la dibuja ni de lo que la envía.

1. **La bóveda** `vault/`Tus credenciales en la memoria, cada una en su propio registro sellado, y el único código que las abre. usa`brands/` `totp/` `crypto/` `hal/` `time/` `platform/` `config/` Probado en un PC

2. **La búsqueda de logotipos** `brands/`Convertir una dirección web en el logotipo que reconoces en la fila, desde una tabla incrustada en el firmware. No se descarga nada. usa`platform/` `config/` Probado en un PC

3. **Las seis cifras** `totp/`El código de doble factor de una cuenta, y la regla de reloj que se niega a mostrarlo cuando la hora no es de fiar. usa`time/` `platform/` `config/` Probado en un PC

4. **Las palabras** `i18n/`Cada frase que el dispositivo puede mostrar, en cada idioma que habla, en una sola tabla. usa`platform/` Probado en un PC

5. **La cerradura** `crypto/`Convertir tu PIN en una clave, y la clave en bytes sellados. La aritmética, y nada más: no sabe nada de pantallas ni de registros. usa`platform/` Probado en un PC

### Hardware y cimiento

Los chips, y la única puerta a los servicios del propio chip. Aquí es donde nueve placas distintas se encuentran con el resto del firmware.

1. **El traductor** `hal/`Un solo vocabulario para nueve placas (dibujar, leer un botón, medir la batería), sea cual sea el chip que haya debajo. usa`drivers/` `time/` `platform/` `config/` Probado en un PC

2. **Los controladores de chip** `drivers/`Un controlador por chip real: pantalla, controlador de energía, reloj, sensor de movimiento, teclado. Escritos aquí en lugar de prestados. usa`time/` `platform/` `config/` Probado en un PC

3. **El reloj y su confianza** `time/`Qué hora cree el dispositivo que es, de dónde viene, y si eso basta para mostrar un código de doble factor. usa`platform/` `config/` Probado en un PC

4. **El cimiento** `platform/`La única puerta a los servicios del propio chip (tareas, tiempo, registro, almacenamiento, sueño) para que nada de arriba los llame directamente. usa`ota/` Probado en un PC

5. **La regla de actualización** `ota/` Si un firmware ofrecido puede instalarse. Aritmética sobre números de versión; no depende absolutamente de nada. no usa ningún otro módulo Probado en un PC

6. **Qué es esta placa** `config/` Los hechos de la placa, fijados al compilar: tamaño de pantalla, tamaño de memoria, qué piezas existen siquiera. no usa ningún otro módulo

Apunta a un bloque, o tócalo, y se encienden los bloques de los que depende. Todos quedan por debajo.

Un bloque por módulo del firmware, en orden de dependencia: todo lo que un bloque necesita queda por debajo. Ningún bloque depende de un bloque que dependa de él, ni directamente ni a través de una cadena de cualquier largo. El dibujo se genera de los #include reales del proyecto, y la compilación falla mientras no concuerde con ellos. Un nivel más fino, entre las carpetas de dentro de los bloques, se cumple en todas partes menos en las 6 carpetas de dentro de `ble/`, que dependen entre sí por diseño y están fijadas por su nombre.

## Dos núcleos, y qué no se permite en cada uno

El chip tiene dos núcleos, y el reparto entre ellos es una regla y no una tendencia.

Núcleo 1

Recoge la última cifra del PIN

Dibuja el progreso, sin esperar a nada

Dibuja la bóveda

Núcleo 0

En reposo

Deriva la clave: PBKDF2-SHA256, 175 000 iteraciones, alrededor de 1,5 s

Abre el índice de la bóveda

Reloj

80 MHz

240 MHz

80 MHz

Un desbloqueo en un ESP32-S3, en esquema: la columna central es la derivación de la clave, alrededor de segundo y medio; las de los lados no están dibujadas a escala.

### Núcleo 1: lo que miras

El bucle principal, la máquina de estados, la pantalla y los botones. No se bloquea nunca esperando a una radio ni al almacenamiento, porque cada milisegundo que pasa esperando es un milisegundo en que notas lento el dispositivo en la mano.

### Núcleo 0: lo que esperas

El host de Bluetooth, el Wi-Fi y el servidor web, el sistema de ficheros y la criptografía. Derivar la clave tarda alrededor de segundo y medio: ocurre aquí, con un indicador de progreso dibujado en el otro núcleo, y al reloj más rápido del chip, mientras que todas las pantallas funcionan al más lento.

Una cola o un cerrojo al que cualquiera de dos tareas pueda llegar primero se construye antes de que exista la tarea que lo consume, o dentro de una llamada que ocurre una sola vez. Mirar si es nulo y crearlo en ese caso es el fallo que parece correcto: los dos núcleos pasan esa comprobación, los dos construyen uno, y a uno de ellos no lo espera nadie.

## Un árbol, nueve placas

Una placa es una configuración, no una copia del firmware.

Cada placa tiene un directorio con su mapa de pines, su pantalla, su controlador de alimentación, su tabla de particiones y sus propias cifras, entre ellas la estimación de sueño que imprime la página de mediciones. El firmware se compila sobre ESP-IDF 6.0.3 sin ningún core de Arduino dentro, en C++26, y con cada aviso del compilador tratado como un error. Un guardián lee las nueve placas y falla ante una incoherencia entre ellas: treinta y tantos invariantes, entre ellos que cada compilación lleve exactamente un factor de trabajo para la derivación de clave, y que la consola serie no pueda compilarse dentro de una imagen de producción.

Cada placa tiene hasta cuatro compilaciones: una abierta de desarrollo, una de depuración que no se publica nunca, una abierta de producción y una endurecida que en su primer arranque quema fusibles de un solo uso, algo que no se puede deshacer. Tres placas no tienen compilación endurecida, porque 4 MB de flash no dan para dos ranuras de actualización y un gestor de arranque firmado.
