agents/ ahora solo contiene carpetas de agentes (config, reglas, prompts). El runtime (Agent, Robot, Runner, registry, handler, commands, llm, memory) vive en devagents/ como package devagents. Cambios: - git mv agents/*.go → devagents/*.go - package agents → package devagents en todos los archivos movidos - Actualizar imports en agents/*/agent.go, cmd/launcher/, dev-scripts/ - Actualizar docs: CLAUDE.md, rules/, docs/e2ee.md, issues pendientes Build y tests pasan sin errores. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4.7 KiB
E2EE (End-to-End Encryption) en agents_and_robots
Resumen
Los bots Matrix usan E2EE via mautrix-go + cryptohelper para comunicarse de forma cifrada.
La implementación usa Olm puro en Go (-tags goolm, sin CGO).
Arquitectura
config.yaml (encryption section)
↓
devagents/runtime.go → Agent.New() llama a InitCrypto()
↓
shell/matrix/client.go → InitCrypto() configura cryptohelper
↓
crypto.db (SQLite) — estado persistente de claves
↓
mautrix sync loop → cifrado/descifrado transparente
Qué guarda la crypto store (crypto.db)
| Dato | Qué es | Cuándo se crea | Cuándo rota |
|---|---|---|---|
| Olm Account | Par de claves Curve25519 del dispositivo | Al primer Init() |
Nunca — es la identidad del dispositivo |
| One-time keys | Claves efímeras para sesiones 1:1 | Al Init() y cuando se agotan |
Automáticamente cuando hay < 50% disponibles |
| Megolm sessions | Claves de grupo para rooms | Al unirse a un room E2EE | Cada ~100 mensajes o ~1 semana |
| Device list cache | Claves públicas de otros dispositivos | Al hacer sync | Se actualiza con cada sync |
| Cross-signing keys | Master, self-signing, user-signing | Al ejecutar cmd/verify |
Manualmente |
Pickle key
El pickle key cifra el material criptográfico en la SQLite. Se configura por agente en .env:
PICKLE_KEY_ASSISTANT_BOT=<hex random 32 bytes>
Y se referencia en config.yaml:
encryption:
enabled: true
store_path: "./agents/assistant-bot/data/crypto/"
pickle_key: "${PICKLE_KEY_ASSISTANT_BOT}"
trust_mode: tofu
Generar un pickle key
openssl rand -hex 32
Por qué NO derivar del access token
Si el token cambia (re-registro, nuevo login), el pickle key cambia y la DB existente se vuelve ilegible. Esto causa el error:
olm account is not marked as shared, but there are keys on the server
Un pickle key fijo por agente en .env evita este problema.
Crypto store por agente
Cada agente debe tener su propia crypto.db para evitar corrupción cruzada:
agents/assistant-bot/data/crypto/crypto.db
agents/asistente-2/data/crypto/crypto.db
No compartir la crypto store entre agentes.
Auto-recovery
Si cryptohelper.Init() falla por inconsistencia (ej: "not marked as shared"),
el runtime borra automáticamente la crypto.db y reintenta. Esto regenera las claves
Olm y requiere re-verificar cross-signing.
Cross-signing y verificación
Elimina warnings de "Encrypted by a device not verified by its owner".
go run -tags goolm ./cmd/verify \
--homeserver https://matrix-af2f3d.organic-machine.com \
--username <bot-id> \
--password <password> \
--token <access_token>
Esto genera y sube cross-signing keys al servidor. Si las claves ya existen, firma el dispositivo actual con la clave existente.
Trust mode
Configurado en config.yaml como trust_mode:
- tofu (Trust-on-First-Use): confía en un dispositivo la primera vez que lo ve. Cambios posteriores generan warnings.
- cross-signing: requiere verificación explícita (no implementado aún).
- manual: cada dispositivo debe verificarse manualmente (no implementado aún).
Build
Siempre compilar con -tags goolm:
go build -tags goolm -o bin/launcher ./cmd/launcher
El driver SQLite se registra como "sqlite3" via modernc.org/sqlite en
cmd/launcher/sqlite.go y cmd/verify/sqlite.go.
Troubleshooting
"olm account is not marked as shared, but there are keys on the server"
La crypto store local está desincronizada con el servidor.
Solución: El runtime intenta auto-recovery. Si falla manualmente:
rm agents/<id>/data/crypto/crypto.db
# Reiniciar el bot
# Re-verificar cross-signing
"database is locked (SQLITE_BUSY)"
Dos procesos están accediendo la misma crypto.db simultáneamente.
Solución: Asegurar que cada agente use su propia store_path y no corran
múltiples instancias del mismo agente con E2EE habilitado sin coordinación.
"unable to decrypt message"
Las claves Megolm de la sesión se perdieron (DB borrada o corrupta).
Solución: Los mensajes cifrados antes del reset son irrecuperables. Los nuevos mensajes se descifrarán normalmente tras regenerar las claves.
Archivos clave
| Archivo | Propósito |
|---|---|
devagents/runtime.go |
Inicializa E2EE por agente |
shell/matrix/client.go |
InitCrypto() — setup de cryptohelper |
cmd/verify/main.go |
Herramienta de cross-signing |
cmd/launcher/sqlite.go |
Registro driver SQLite |
internal/config/schema.go |
Schema de EncryptionCfg |
agents/*/config.yaml |
Configuración E2EE por agente |