Skip to content

fix(security): Encrypt Slack and Telegram bot tokens at rest (Invariant #13) #453

@vybe

Description

@vybe

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_tokenTEXT NOT NULL (src/backend/db/schema.py:539, db/migrations.py:537)
  • slack_workspaces.bot_tokenTEXT 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

  • Bot tokens for Slack (both tables) and Telegram are encrypted at rest
  • Existing rows migrate cleanly (no service disruption after backend restart)
  • Tests cover: create → read round-trip, decryption failure handling, legacy plaintext row migration (if one-shot approach)
  • architecture.md credentials section is accurate
  • /validate-architecture no longer flags slack_bot_token under Invariant feat: SMARTS trading pipeline with Telegram notifications and Miro visualization #13

References

Metadata

Metadata

Assignees

Labels

priority-p1Critical pathsecuritySecurity vulnerabilityseverity-highHigh severity security findingstatus-in-devMerged to dev, awaiting release cut to main

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions