fix(security): ceo-web resuelve sus secretos desde 1Password al arrancar
El problema
launchd cargaba ~/.config/cmoagent/web.env: 31 variables, 12 de ellas secretos en claro — Slack (signing secret + bot token), Resend, GitLab, Postiz, YouTube, Freepik, la contraseña del portal, el cookie secret y la URL de Postgres. En un fichero fuera de todo repo.
Ese fichero fue el cuarto vector de la auditoría de gasto del 2026-08-04: llevaba la master key de LiteLLM y, al no estar versionado ni escaneado, no aparecía en ningún barrido. Se descubrió solo cuando el proxy volvió a registrar 58 llamadas sin atribuir con los repos ya limpios.
La migración
Los 12 secretos ya existían en 1Password con refs en .env.tpl, y sus valores coincidían exactamente. La plantilla reusa esos mismos items: una sola fuente por secreto, sin duplicar nada.
-
deploy/web.env.tpl— plantilla versionada: secretos por referencia, config no sensible literal. -
scripts/start-ceo-web.sh— resuelve con el service account (desatendido, sin Touch ID) y arranca. Si 1Password no responde, reutiliza el último.resolved: un fallo del gestor de secretos no debe tirar el portal. - El plist llama al script.
web.envretirado.
Dos trampas, un arranque fallido cada una
-
op injectresuelve referencias incluso dentro de comentarios. La cabecera de la plantilla las llevaba en prosa y abortaba la resolución entera. El.env.tplde este repo ya lo documentaba — no lo leí a tiempo. -
op inject -osobre un fichero que ya existe pide confirmación interactiva. Bajo launchd no hay TTY, así que fallaba en silencio. Ahora escribe a stdout.
Test plan
-
La plantilla resuelve a las mismas 31 claves con los mismos valores que el fichero que sustituye (comparación programática, cero diferencias) -
launchctl kickstart→entorno resuelto desde 1Password, proceso vivo -
Portal en :8788→ HTTP 401 (auth activa, comportamiento correcto) -
Arranque repetido tras retirar web.env→ sigue funcionando -
ps ewwdel proceso: sin la master key, con la VK de CMOAgent