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>
99 lines
4.4 KiB
Markdown
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.
|