feat: implement end-to-end encryption (E2EE) support for agents with configuration and documentation updates

This commit is contained in:
2026-03-05 00:06:32 +00:00
parent 1e5103eb70
commit 54fe479792
11 changed files with 227 additions and 21 deletions
+8 -6
View File
@@ -111,7 +111,8 @@ matrix:
encryption:
enabled: true
store_path: "./data/crypto/"
store_path: "./agents/<agent-id>/data/crypto/"
pickle_key_env: PICKLE_KEY_<AGENT_UPPER>
trust_mode: tofu
```
@@ -221,15 +222,15 @@ go run -tags goolm ./cmd/verify \
3. Las sube al homeserver usando UIA con la password del bot
4. Firma el device del bot con la self-signing key
**Después de verificar:** Limpiar el crypto store temporal si se usó uno diferente al del agente.
**Importante:** Si se cambia la password del bot (admin API), el token anterior se invalida. Hay que:
1. Re-login para obtener nuevo token
2. Actualizar `MATRIX_TOKEN_<AGENT>` en `.env`
3. Actualizar `device_id` en `config.yaml`
4. Borrar el crypto store viejo (`data/crypto/`)
4. Borrar el crypto store viejo (`agents/<id>/data/crypto/crypto.db`)
5. Re-ejecutar `cmd/verify`
**Nota:** El pickle key (`PICKLE_KEY_<AGENT>`) NO cambia al rotar el token. Solo se regenera si se pierde. Ver `docs/e2ee.md`.
## Paso 6: Arrancar el agente
```bash
@@ -251,7 +252,7 @@ tail -f run/<agent-id>.log
**Logs esperados al arrancar correctamente:**
```
{"level":"INFO","msg":"initializing e2ee","store":"data/crypto/crypto.db"}
{"level":"INFO","msg":"initializing e2ee","store":"agents/<id>/data/crypto/crypto.db"}
{"level":"INFO","msg":"e2ee ready"}
{"level":"INFO","msg":"agent starting","id":"<agent-id>","tools":["current_time","matrix_send"]}
{"level":"INFO","msg":"starting matrix sync"}
@@ -305,7 +306,8 @@ tail -f run/<id>.log
|----------|-------|----------|
| `env var ... is not set` | La regex del `.env` loader no matchea | Verificar que el nombre de la var solo usa `[A-Z0-9_]` |
| `M_UNKNOWN_TOKEN` | Token invalidado (password cambiada) | Re-login, actualizar `.env` |
| `mismatching device ID` | Crypto store con device viejo | Borrar `data/crypto/`, actualizar `device_id` en config |
| `mismatching device ID` | Crypto store con device viejo | Borrar `agents/<id>/data/crypto/crypto.db`, actualizar `device_id` en config |
| `olm account not marked as shared` | Crypto store inconsistente | Auto-recovery lo resuelve al reiniciar. Si persiste: borrar crypto.db |
| `"Encrypted by device not verified"` | Falta cross-signing | Ejecutar `cmd/verify` |
| Bot no responde | Reglas no matchean | Verificar que hay regla catch-all para DMs/mentions |
| `no rules registered for agent` | ID no está en `rulesRegistry` | Añadir en `cmd/launcher/main.go` |
+153
View File
@@ -0,0 +1,153 @@
# 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)
agents/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`:
```env
PICKLE_KEY_ASSISTANT_BOT=<hex random 32 bytes>
```
Y se referencia en `config.yaml`:
```yaml
encryption:
enabled: true
store_path: "./agents/assistant/data/crypto/"
pickle_key: "${PICKLE_KEY_ASSISTANT_BOT}"
trust_mode: tofu
```
### Generar un pickle key
```bash
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/data/crypto/crypto.db
agents/asistente2/data/crypto/crypto.db
agents/devops/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".
```bash
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`:
```bash
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:
```bash
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 |
|---------|-----------|
| `agents/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 |