Files
hammer/docs/07-agent-bus.md
T
sergioandClaude Opus 4.8 8bf1623044 Scaffold inicial: workspace Rust + SDDs completos
Arranque del proyecto hammer (distro AI-nativa: laboratorio funcional en el
sótano, terminal mutable clásica arriba, integración de IA programadora).

- Workspace Rust (compila, tests verdes): hammer-core, hammer-build,
  hammer-cli (bin `hammer`), hammerd.
- SDDs 00-10 + 6 ADRs en docs/ con toda la arquitectura.
- Esqueletos navegables mapeados a las fases del roadmap; Fase 0/1 listas
  para implementar el sandbox real.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 19:06:04 +00:00

99 lines
4.4 KiB
Markdown

# 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:
```sh
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](08-ai-integration.md)),
- **versión** en el handshake.
## 3. Protocolo del bus de agente
### Handshake
```json
{"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
```rust
// 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: `hammer ctl "start web"` (humano) · la IA usa el socket directamente.