Problem
Bot tokens for external messaging integrations are stored as plaintext in SQLite, violating Architectural Invariant #13 (Credentials: never stored in DB; transient secrets in Redis; encrypted exports use AES-256-GCM).
Confirmed plaintext storage
slack_link_connections.slack_bot_token — TEXT NOT NULL (src/backend/db/schema.py:539, db/migrations.py:537)
slack_workspaces.bot_token — TEXT NOT NULL (db/migrations.py:863)
telegram_chat_links bot tokens — plaintext (see adapters/telegram_adapter.py and db.get_telegram_bot_token)
Writes happen in db/slack.py:create_slack_connection and matching Telegram paths without any encryption layer.
Architecture doc is inaccurate
docs/memory/architecture.md claims slack_workspaces stores "encrypted bot tokens (encrypted at application layer)" — this is false and should be corrected as part of this fix.
Impact
Slack/Telegram bot tokens grant full workspace messaging access. A DB leak (backup exfil, SQLite file access, read-only replica exposure) reveals live credentials that can impersonate Trinity agents in user workspaces. Trinity already has an encryption primitive: services/credential_encryption.py (AES-256-GCM, used for .credentials.enc).
Scope
- Encrypt bot tokens at write, decrypt at read for:
slack_link_connections.slack_bot_token
slack_workspaces.bot_token
- Telegram bot token storage (
telegram_chat_links or equivalent)
- Reuse
services/credential_encryption.py (AES-256-GCM) or add an equivalent helper keyed off SECRET_KEY / dedicated env var.
- Migration strategy for existing rows:
- Option 1: one-shot migration that encrypts in place at startup (needs versioned migration in
db/migrations.py).
- Option 2: lazy migration — encrypt on next write, tolerate both formats on read. Simpler but leaves plaintext around until rotation.
- Update
docs/memory/architecture.md to reflect actual state (either correct the "encrypted" claim with the real implementation, or remove it if Option 2 lazy migration).
Acceptance Criteria
References
Problem
Bot tokens for external messaging integrations are stored as plaintext in SQLite, violating Architectural Invariant #13 (Credentials: never stored in DB; transient secrets in Redis; encrypted exports use AES-256-GCM).
Confirmed plaintext storage
slack_link_connections.slack_bot_token—TEXT NOT NULL(src/backend/db/schema.py:539,db/migrations.py:537)slack_workspaces.bot_token—TEXT NOT NULL(db/migrations.py:863)telegram_chat_linksbot tokens — plaintext (seeadapters/telegram_adapter.pyanddb.get_telegram_bot_token)Writes happen in
db/slack.py:create_slack_connectionand matching Telegram paths without any encryption layer.Architecture doc is inaccurate
docs/memory/architecture.mdclaimsslack_workspacesstores "encrypted bot tokens (encrypted at application layer)" — this is false and should be corrected as part of this fix.Impact
Slack/Telegram bot tokens grant full workspace messaging access. A DB leak (backup exfil, SQLite file access, read-only replica exposure) reveals live credentials that can impersonate Trinity agents in user workspaces. Trinity already has an encryption primitive:
services/credential_encryption.py(AES-256-GCM, used for.credentials.enc).Scope
slack_link_connections.slack_bot_tokenslack_workspaces.bot_tokentelegram_chat_linksor equivalent)services/credential_encryption.py(AES-256-GCM) or add an equivalent helper keyed offSECRET_KEY/ dedicated env var.db/migrations.py).docs/memory/architecture.mdto reflect actual state (either correct the "encrypted" claim with the real implementation, or remove it if Option 2 lazy migration).Acceptance Criteria
architecture.mdcredentials section is accurate/validate-architectureno longer flagsslack_bot_tokenunder Invariant feat: SMARTS trading pipeline with Telegram notifications and Miro visualization #13References
src/backend/services/credential_encryption.py