Files
takana/docs/07-agent-bus.md
Sergio 97ceb72411 takana etapa 5b: los 59 docs de diseño, runbooks y ADR
645 líneas. Los ADR entran porque en este repo SON documentos vivos, no
registros inmutables: el 0013 tiene 5 commits, el 0009 dos. Eso se comprobó
antes de decidir, no se asumió por convención general.

EXCLUIDOS por ser REGISTRO o generado: docs/evidencia/ (6), el HANDOFF de la
noche de KDE (1) y docs/state/ (24, se regenera solo). Reescribir un comando
dentro de una evidencia la falsifica.

Y el ADR 0016 se excluye de todo barrido, con un aviso adentro para el próximo
que barra: habla SOBRE el renombre, así que necesita seguir diciendo 'hammer'.
El barrido se lo llevó puesto y lo dejó titulado 'Renombre del sistema: takana
→ takana'; revertido.

Congelados, verificados uno por uno con controles: /opt/hammer, /var/lib/hammer,
/usr/bin/hammer, /mnt/vvv/hammer, la URL de gitea, hammer-farm.service,
hammer-live-install.sh, BRIEFING-hammer.md, hammerd, hammer-recover y
HAMMER_LIVE.
2026-09-09 19:25:51 +00:00

4.4 KiB

SDD 07 — Bus de init y de agente

El sistema se controla por tuberías UNIX puras, no por parseo de archivos .service complejos. Hay dos canales, con propósitos distintos:

  1. /run/init.control — FIFO de control humano, texto crudo. Ergonomía máxima.
  2. /run/agent.sock — socket de control de agente (IA, scripts), JSON-líneas con framing y autenticación. Parseable y fiable.

1. El init por eventos UNIX

Tu init no lee configuraciones pesadas: expone una API por FIFO. Cualquier proceso —un script, un binario, o tú tecleando— controla el estado del sistema enviando bytes crudos:

echo "restart network" > /run/init.control
echo "start web"       > /run/init.control
echo "stop  web"       > /run/init.control

Los servicios no se declaran en archivos pesados; se inyectan dinámicamente. El estado del sistema es un flujo vivo y maleable que manipulas con herramientas estándar (cat, echo, grep). Los servicios viven como directorios con un script run ejecutable (estilo s6/runit), no como unidades declarativas opacas:

/etc/service/web/run        # script ejecutable; el init lo supervisa
/etc/service/network/run

2. Por qué un segundo canal para la IA

El FIFO crudo es perfecto para humanos pero malo como API de máquina: no tiene framing fiable, ni forma de devolver eventos correlacionados, ni de saber quién habla. La IA necesita:

  • enviar comandos estructurados y recibir eventos correlacionados (éxito/fallo de un build, crash de un servicio),
  • autenticación del peer (que no cualquier proceso dispare builds o inyecte binarios),
  • un protocolo pequeño, legible y versionado.

Por eso /run/agent.sock es un socket UNIX con:

  • framing = JSON-líneas (un objeto JSON por línea, \n-terminado),
  • auth = SO_PEERCRED (UID/GID/PID del peer verificados por el kernel) + capacidades que el humano concede explícitamente (ver SDD 08),
  • versión en el handshake.

3. Protocolo del bus de agente

Handshake

{"t":"hello","ver":1,"client":"claude-agent"}
{"t":"welcome","ver":1,"caps":["compile","query"]}   // caps según lo que el humano concedió

Comandos (cliente → hammerd)

Comando Forma Efecto
COMPILE {"t":"compile","recipe":{…}} o {"repo","commit","patch","build"} dispara un build en el lab
INJECT {"t":"inject","artifact":"b3:…","target":"/bin/grep","overlay":"<id>"} hidrata en un overlay (o real si hay cap)
QUERY {"t":"query","what":"file","path":"/etc/network.conf"} estado de un archivo/servicio/artefacto
INIT {"t":"init","cmd":"start web"} proxy autenticado al control del init

Eventos (hammerd → cliente)

Evento Forma Significado
BUILD_READY {"t":"build_ready","artifact":"b3:…","recipe":"grep"} build terminó OK
BUILD_FAILED {"t":"build_failed","recipe":"grep","log_tail":"…"} build falló, con cola de log
CRASHED {"t":"crashed","service":"web","code":127} un servicio supervisado murió
MODIFIED {"t":"modified","path":"/bin/grep","by":{…}} el diario registró una mutación

Ejemplo del bucle de autorreparación: la IA modifica el binario del servidor web, este falla al arrancar; hammerd emite {"t":"crashed","service":"web","code":127}; la IA, escuchando, lee el log (texto plano en /var/log), corrige la fuente, recompila, y reenvía start web. Para el humano, el sistema "se autorreparó" — pero cada paso quedó en el bus y en el diario.

4. Seguridad del bus

  • Sin auth, sin comandos. Conexión que no completa hello válido se cierra.
  • Capacidades mínimas. Por defecto un agente sólo obtiene query. compile, inject (a overlay) e inject-real (al FHS real) se conceden explícita y separadamente.
  • inject-real es especial. Inyectar al sistema real (no a overlay) requiere una capacidad aparte y, por política, confirmación humana. El flujo recomendado de la IA es siempre overlay
    • commit humano.
  • Todo comando con efecto se registra (en el diario y/o un audit log del bus).

5. Interfaz

// hammerd
pub async fn serve_agent_bus(sock: &Path, caps_policy: CapsPolicy) -> Result<()>;
pub fn init_control_send(line: &str) -> Result<()>;   // escribe en /run/init.control

CLI de conveniencia: takana ctl "start web" (humano) · la IA usa el socket directamente.