"""Per-client cloud visibility whitelist for the External API. Adds ``api_clients.cloud_visibility`` (JSON, nullable): {"providers": ["aws", "gcp"], "subscription_ids": ["", "…"]} The two lists are a UNION — "every subscription of these providers, plus these individually named ones". ``NULL`` = unrestricted, which is exactly the behaviour every existing client has today, so this migration is additive and changes no live grant. Why: a scope (``cloud.subscriptions:read``) says what KIND of cloud data a machine client may read, never WHICH accounts. That was tolerable while the surface exposed inventory metadata; it stops being tolerable once the same credential plane serves cloud BILLING, where one leaked token would otherwise expose the company's entire cloud spend. Revision ID: 470 Revises: 469 """ import sqlalchemy as sa from alembic import op revision = "470" down_revision = "469" branch_labels = None depends_on = None def upgrade() -> None: op.add_column( "api_clients", sa.Column("cloud_visibility", sa.JSON(), nullable=True), ) def downgrade() -> None: op.drop_column("api_clients", "cloud_visibility")