"""control0006_fernet_key_version Adds the Fernet key-version stamp to the `tenants` table: - key_version INT NOT NULL DEFAULT 1 MT-23: credentials are encrypted under a single control-plane Fernet key, and before this there was no way to tell which rows had been re-encrypted after a key change — rotation was all-or-nothing and unverifiable. Stamping the version each time a row is written makes rotation progress a plain query: SELECT COUNT(*) FROM tenants WHERE key_version < ; Existing rows default to 1, which matches the single key in use before this migration, so nothing needs re-encrypting to adopt it. Guarded by an INFORMATION_SCHEMA existence check, so re-running is safe. Revision ID: control0006_fernet_key_ver Revises: control0005_superadmin_mfa Create Date: 2026-08-21 """ from alembic import op import sqlalchemy as sa # NOTE: revision ids must fit VARCHAR(32) — 'control0006_fernet_key_ver' is 26. revision = 'control0006_fernet_key_ver' down_revision = 'control0005_superadmin_mfa' branch_labels = None depends_on = None def _column_exists(table, column): result = op.get_bind().execute(sa.text( "SELECT COUNT(*) FROM information_schema.COLUMNS " "WHERE TABLE_SCHEMA = DATABASE() " " AND TABLE_NAME = :tbl " " AND COLUMN_NAME = :col" ), {'tbl': table, 'col': column}) return result.scalar() > 0 def upgrade(): if not _column_exists('tenants', 'key_version'): op.add_column('tenants', sa.Column('key_version', sa.Integer(), nullable=False, server_default='1')) def downgrade(): if _column_exists('tenants', 'key_version'): op.drop_column('tenants', 'key_version')