5f651ce4b3
Activar AuditCfg existente en schema.go (nunca implementada) para escribir eventos auditables a JSONL y opcionalmente a room Matrix. Añadir comando !metrics que agrega datos del log del día actual (tokens, latencia, errores) usando los helpers de query existentes.
178 lines
7.6 KiB
Markdown
178 lines
7.6 KiB
Markdown
# 0035 — Observabilidad activa: audit trail + comando !metrics
|
|
|
|
**Estado:** pendiente
|
|
|
|
## Objetivo
|
|
|
|
Activar la infraestructura de auditoría (`AuditCfg`) que ya está definida en `internal/config/schema.go` pero nunca implementada, y añadir un comando `!metrics` que agregue datos del log del día actual. Ambas features usan infraestructura existente (JSONL logs, config schema, command system) sin dependencias nuevas.
|
|
|
|
## Contexto
|
|
|
|
- `AuditCfg` lleva definida desde el schema original pero el código nunca la consume: no hay writer, no hay emisión de eventos, no hay integración con el runtime.
|
|
- Los logs JSONL ya contienen métricas útiles (`duration_ms`, `tokens_used`, `tool_exec_*`, `command_received`) pero no hay forma de consultarlas sin parsear archivos manualmente.
|
|
- `shell/logger/query.go` ya tiene helpers para leer y filtrar logs por fecha/campo.
|
|
- El command system (`!status`, `!info`) ya existe y es extensible via built-in.
|
|
|
|
## Arquitectura
|
|
|
|
### Fase 1: Audit trail
|
|
|
|
```
|
|
shell/audit/ NEW — audit event writer (archivo JSONL + opcionalmente room Matrix)
|
|
shell/audit/writer.go NEW — AuditWriter: escribe eventos a archivo y/o room
|
|
```
|
|
|
|
Integración en el runtime existente:
|
|
```
|
|
agents/handler.go MOD — emitir eventos audit en puntos clave
|
|
agents/runtime.go MOD — inicializar AuditWriter si cfg.Security.Audit.Enabled
|
|
tools/registry.go MOD — emitir evento audit en tool_exec
|
|
```
|
|
|
|
**Pure core / impure shell:**
|
|
- No se añade nada a `pkg/` — los eventos audit son side effects puros (escritura a archivo/Matrix)
|
|
- `shell/audit/` es 100% impuro: escribe a disco y opcionalmente envía mensajes Matrix
|
|
|
|
### Fase 2: Comando !metrics
|
|
|
|
```
|
|
agents/commands.go MOD — añadir cmdMetrics como built-in
|
|
pkg/command/builtins.go MOD — añadir spec de !metrics
|
|
```
|
|
|
|
El comando lee los JSONL del día actual usando `shell/logger/query.go` (ya existente) y calcula agregados en memoria. No persiste nada, no crea tablas, no necesita SQLite.
|
|
|
|
## Tareas
|
|
|
|
### Fase 1 — Audit trail
|
|
|
|
- [ ] **1.1** Crear `shell/audit/writer.go` con `AuditWriter` struct:
|
|
- Constructor `New(cfg AuditCfg, matrixSender func(roomID, msg string), logger *slog.Logger) *AuditWriter`
|
|
- Método `Emit(event AuditEvent)` que escribe a `LogFile` (JSONL append) y opcionalmente envía a `LogToRoom` (room Matrix)
|
|
- `AuditEvent` struct: `{ Time, AgentID, EventType, SenderID, RoomID, Detail string }`
|
|
- Filtrado por `Include` (lista de event types a auditar; vacío = todos)
|
|
- Event types iniciales: `command_exec`, `tool_exec`, `llm_request`, `llm_error`, `message_received`
|
|
|
|
- [ ] **1.2** Crear `shell/audit/writer_test.go`:
|
|
- Test de escritura a archivo (verificar formato JSONL)
|
|
- Test de filtrado por `Include` (solo emite los tipos configurados)
|
|
- Test con `LogFile` vacío (no escribe a archivo, solo room)
|
|
- Test con `LogToRoom` vacío (solo escribe a archivo)
|
|
|
|
- [ ] **1.3** Integrar `AuditWriter` en `agents/runtime.go`:
|
|
- En `New()`: si `cfg.Security.Audit.Enabled`, crear `AuditWriter` y guardarlo en el struct `Agent`
|
|
- Pasar `matrixSender` como closure que usa el cliente Matrix del agente
|
|
- Si audit no está habilitado, `AuditWriter` es nil (los call sites hacen nil-check)
|
|
|
|
- [ ] **1.4** Emitir eventos en los puntos clave:
|
|
- `agents/handler.go` → `message_received` (sender, room, is_dm)
|
|
- `agents/handler.go` → `command_exec` (command name, sender)
|
|
- `tools/registry.go` → `tool_exec` (tool name, duration, success/error)
|
|
- `shell/llm/` → `llm_request` (provider, model, tokens) y `llm_error` (provider, error)
|
|
|
|
### Fase 2 — Comando !metrics
|
|
|
|
- [ ] **2.1** Añadir spec en `pkg/command/builtins.go`:
|
|
```go
|
|
{Name: "metrics", Description: "Métricas agregadas del día actual", Usage: "!metrics"}
|
|
```
|
|
|
|
- [ ] **2.2** Implementar `cmdMetrics` en `agents/commands.go`:
|
|
- Leer logs del día actual con `logger.ReadDayLogs(logDir, agentID, time.Now())`
|
|
- Calcular: total mensajes recibidos, comandos ejecutados, llamadas LLM (count + tokens totales + latencia media), llamadas a tools (count + errores), errores totales
|
|
- Formatear como markdown table para Matrix
|
|
- Ejemplo de output:
|
|
```
|
|
**Métricas de hoy (2026-04-09):**
|
|
|
|
| Métrica | Valor |
|
|
|---------|-------|
|
|
| Mensajes recibidos | 42 |
|
|
| Comandos ejecutados | 15 |
|
|
| Llamadas LLM | 27 |
|
|
| Tokens totales | 45,230 |
|
|
| Latencia LLM media | 1,250 ms |
|
|
| Tool calls | 8 |
|
|
| Tool errors | 1 |
|
|
| Errores totales | 2 |
|
|
| Uptime | 6h 30m |
|
|
```
|
|
|
|
- [ ] **2.3** El handler necesita acceso al `logDir` del agente — pasar via config o campo en Agent struct (ya existe `a.cfg` con el agent ID, solo falta saber el baseDir de logs)
|
|
|
|
### Fase 3 — Tests y cleanup
|
|
|
|
- [ ] **3.1** Tests para `cmdMetrics`: crear logs JSONL de ejemplo en tmpdir, verificar que los agregados son correctos
|
|
- [ ] **3.2** Test de integración: `AuditWriter` + handler emite eventos reales a archivo temporal
|
|
- [ ] **3.3** Documentar en `docs/security.md` la sección de audit trail (config YAML de ejemplo)
|
|
|
|
## Ejemplo de uso
|
|
|
|
### Audit trail
|
|
|
|
Config en `agents/asistente-2/config.yaml`:
|
|
```yaml
|
|
security:
|
|
audit:
|
|
enabled: true
|
|
log_file: "logs/asistente-2/audit.jsonl"
|
|
log_to_room: "!audit-room:matrix-af2f3d.organic-machine.com"
|
|
include:
|
|
- command_exec
|
|
- tool_exec
|
|
- llm_error
|
|
```
|
|
|
|
Resultado en `audit.jsonl`:
|
|
```json
|
|
{"time":"2026-04-09T10:30:00Z","agent_id":"asistente-2","event":"command_exec","sender":"@user:matrix","room":"!abc:matrix","detail":"!status"}
|
|
{"time":"2026-04-09T10:30:05Z","agent_id":"asistente-2","event":"tool_exec","sender":"@user:matrix","room":"!abc:matrix","detail":"http_get duration=350ms ok"}
|
|
```
|
|
|
|
### Comando !metrics
|
|
|
|
```
|
|
Usuario: !metrics
|
|
Bot:
|
|
**Métricas de hoy (2026-04-09):**
|
|
|
|
| Métrica | Valor |
|
|
|---------|-------|
|
|
| Mensajes recibidos | 42 |
|
|
| Comandos ejecutados | 15 |
|
|
| Llamadas LLM | 27 |
|
|
| Tokens totales | 45,230 |
|
|
| Latencia LLM media | 1,250 ms |
|
|
| Tool calls | 8 |
|
|
| Tool errors | 1 |
|
|
| Errores totales | 2 |
|
|
| Uptime | 6h 30m |
|
|
```
|
|
|
|
## Decisiones de diseño
|
|
|
|
1. **Audit separado de logs normales**: los logs de runtime son para debugging (alto volumen, retención corta). El audit trail es para compliance/revisión (eventos selectivos, retención configurable independiente).
|
|
|
|
2. **Sin SQLite para métricas**: el comando `!metrics` calcula en memoria leyendo JSONL del día. Con los volúmenes actuales (~cientos de eventos/día por agente), esto es instantáneo. Si escala, se puede cachear o migrar a SQLite en un issue futuro.
|
|
|
|
3. **AuditWriter acepta matrixSender como función**: evita acoplar `shell/audit/` con el cliente Matrix directamente. Sigue el patrón de inyección de dependencias del proyecto.
|
|
|
|
4. **Include como allowlist**: lista vacía = auditar todo. Esto es deny-by-default invertido (opt-in por tipo de evento) para evitar audit logs gigantes.
|
|
|
|
## Prerequisitos
|
|
|
|
Ninguno. Todo usa infraestructura existente:
|
|
- `AuditCfg` en `internal/config/schema.go`
|
|
- `shell/logger/query.go` para leer JSONL
|
|
- `pkg/command/builtins.go` para registrar `!metrics`
|
|
- `agents/commands.go` para implementar el handler
|
|
|
|
## Riesgos
|
|
|
|
| Riesgo | Mitigación |
|
|
|--------|------------|
|
|
| Audit file crece sin límite | Usar el mismo `DailyRotatingWriter` de `shell/logger/` o rotación externa (logrotate) |
|
|
| `LogToRoom` falla (room no existe) | Log warning y continuar — audit a archivo no debe fallar por Matrix |
|
|
| `!metrics` lento con logs muy grandes | Los JSONL se rotan a 50MB max. Un día normal tiene KB-pocos MB. Aceptable. |
|
|
| Audit de `message_received` loguea contenido sensible | El `Detail` solo incluye metadata (sender, room, is_dm), nunca el body del mensaje |
|