Skip to content

Operations: Upgrade and Compatibility Notes

2026-09 canonical Manager usage snapshot scopes

Migration 0124_canonical_manager_usage_scopes replaces accepted alternative spellings of Manager context IDs in bucket usage snapshots, such as 0001, conn-0008, and s3u-0003, with the resolved identifiers 1, conn-8, and s3u-3. Account, connection, and S3-user scopes stay separate. Direct-session IDs, unrecognized scopes, other workspaces, and historical audit rows are unchanged.

When several snapshots map to the same context and bucket, the migration keeps the newest calculated_at, breaking ties by updated_at and then the highest row ID. It preserves that row's statistics, warnings, and timestamps and removes only its superseded duplicate snapshots. Back up the database first; downgrade leaves canonical snapshots intact and cannot restore discarded duplicates or their original selector spellings.

Stop old backend instances and ongoing usage scans before applying the migration, then deploy the matching backend so subsequent scans use the resolved context ID. No runtime alias lookup is retained. Native storage permissions and the accepted request selectors are unchanged.

2026-09 least-privilege containers and strict network policy

The application images now run with fixed non-root identities and a read-only root filesystem. nginx moved from container port 80 to 8080; Compose host port 8080 and the Helm frontend Service port 80 are unchanged. Replace the old runtime-installing Compose scheduler with the matching dedicated scheduler image. Preserve the backend /data volume and provide a writable /tmp only; do not make the image root writable to work around startup failures.

Before the first rootless Compose start, stop the old backend and inspect the existing named volume ownership. If its files were created by root, perform a single controlled ownership migration to UID/GID 10001:10001 using the exact volume name from docker volume ls; back up the volume first. Do not recreate or replace it, because it contains the SQLite database and historical encrypted credentials.

Helm enables the strict NetworkPolicy profile by default and intentionally refuses incomplete values. Before upgrading, inventory the namespace/pod labels of the ingress controller and DNS service, the exact proxy pod CIDRs, every external database CIDR, and all private storage or webhook target CIDRs and ports. Configure networkPolicy.ingressController, networkPolicy.dns, networkPolicy.publicHttpsEgress, and any required networkPolicy.privateEgress entries. Public HTTPS ranges must explicitly exclude private, loopback, link-local, metadata, multicast, and reserved networks. Render and review the resulting policies before applying them.

This is a breaking deployment boundary: old Helm values that lack selectors or egress rules no longer render in the strict profile. Disabling the profile is reserved for an isolated migration validation; production operators must complete the inventory rather than leave it disabled.

2026-09 trusted proxy boundary is mandatory

Production startup now refuses an empty TRUSTED_PROXY_CIDRS. The Helm chart requires the structured backend.trustedProxyCidrs value and rejects a raw backend.env.TRUSTED_PROXY_CIDRS override. Before upgrading, identify the actual direct peers that connect to the backend and configure their narrow CIDRs. Requests from any other peer ignore X-Forwarded-For; trusted chains continue to be evaluated from right to left so an injected left-most value cannot select a new rate-limit identity.

For Docker Compose with a fixed proxy address, a /32 entry is preferred. For Kubernetes, use the verified ingress-controller pod subnet or addresses rather than the entire cluster or private address space. The chart intentionally fails to render until this boundary is supplied.

2026-09 outbound target allowlists

Production now fails closed for user-controlled outbound destinations. Populate USER_SUPPLIED_S3_ENDPOINT_ALLOWED_HOSTS for user-created S3 connections and BUCKET_MIGRATION_WEBHOOK_ALLOWED_HOSTS for migration callbacks before the upgrade. A plain entry authorizes only that exact hostname; subdomains require an explicit *.example.com entry, which does not authorize the apex.

Run the preflight against the existing database before deploying:

cd backend
python -m app.scripts.preflight_outbound_targets

The command prints only uncovered hostnames. Existing user-created manual connections outside the allowlist remain stored but cannot be used until the operator updates the allowlist. Admin-registered storage endpoints remain available. Existing webhook URLs outside the webhook allowlist cannot be saved or delivered. Production webhooks use HTTPS unless the private-target option and an explicit host allowlist entry are both configured.

2026-09 Manager and Portal account-role split

Migration 0122_split_account_access_roles replaces the single association role with independent manager_role and portal_role columns on direct user and UI-group links. It also removes the obsolete BucketReef users.is_root and user_s3_accounts.is_root columns. Ceph account-root identities, RGW account credentials, and the corresponding technical sessions are unchanged.

The migration maps account_administrator and former root links to Manager administrator only; portal_user and portal_manager map to the matching Portal role only. It adds database constraints requiring at least one role and allowing Manager Browser data access only with a Manager administrator role. Backend, frontend, automation clients, and migration must be deployed together because the old role contract is rejected.

Alembic does not contact RGW, delete AccountIAMUser rows, reconcile Portal IAM, or install a post-migration cleanup hook. Historical IAM identities are left untouched. This project is treated as not yet deployed in production; if an existing development environment is inconsistent, recreate its database and RGW test resources instead of adding migration compatibility machinery.

Normal runtime synchronization remains active for explicit Portal-role grants and revocations after the migration. Downgrade is refused when any association contains both a Manager and Portal role because the former schema cannot represent that state without losing access information.

2026-08 first-administrator bootstrap cutover

Migration 0119_first_admin_bootstrap adds the one-time bootstrap singleton. The former SEED_ + SUPER_ADMIN_{EMAIL,PASSWORD,FULL_NAME,MODE} environment family and automatic startup creation have been removed without aliases or runtime warnings.

Before upgrading a deployment that depended on those variables, use the old release to confirm the administrator exists. If the database is empty, deploy the migration/backend, then immediately either issue a temporary URL with python -m app.scripts.issue_first_admin_bootstrap or create the administrator with python -m app.scripts.create_first_admin; only then remove the former variables from Compose, Helm values and secret management. Existing user databases do not reopen bootstrap. Existing-administrator recovery remains a separate command and never creates a new first user.

Deploy migration, backend and frontend together. The Admin onboarding response now exposes endpoint_configured, storage_access_configured and complete; clients using the removed seed-account readiness field must update atomically.

2026-08 authentication cutover

Migrations 0107 through 0110 replace legacy browser Bearers and refresh rows with revocable database sessions, single-use refresh families, separate external identities, WebAuthn, recovery codes, persistent rate limits, and scoped API tokens. The cutover revokes every existing UI session and API token, deletes every S3 session and its credential material, and purges OIDC states.

This is a backend/frontend/configuration big-bang boundary; there is no legacy Bearer compatibility. Verify a restorable backup and complete credential re-encryption before deployment, then set BUCKETREEF_DB_BACKUP_VERIFIED=true for the migration run. The 0109 downgrade deliberately fails because it cannot reconstruct erased secrets. Follow Authentication security and cutover.

2026-08 strict API object contracts

All backend application request and response objects now reject unknown properties. Canonical frontend requests are unchanged, but an API request that still sends a removed field now receives 422 instead of silently discarding it. Deploy the backend and frontend together, and remove obsolete properties from any local scripts before upgrading.

2026-08 canonical object data plane

The unused Manager object upload, folder creation, deletion, and presigned download routes under /api/manager/buckets/{bucket_name}/objects/* have been removed. The Manager bucket detail keeps its read-only object listing endpoint. Use the Browser object endpoints for data-plane operations; there is no Manager compatibility alias. Deploy the backend and frontend together, and migrate any local functional tooling to the Browser routes before upgrading.

2026-08 strict environment-managed provider configuration

Entries nested under OIDC_PROVIDERS, LDAP_PROVIDERS, and ENV_STORAGE_ENDPOINTS now reject unknown fields instead of silently ignoring them. Before deploying, remove obsolete fields and correct misspelled names in those environment values. Valid configurations and the top-level handling of unrelated process environment variables are unchanged.

2026-08 canonical Storage Endpoint feature configuration

Migration 0115_canonical_storage_endpoint_features rewrites every persisted Storage Endpoint feature document under the single features root. It converts the former health-check url and endpoint aliases to healthcheck_url, normalizes configured URLs and modes, and removes unknown feature names or fields that the runtime previously ignored.

After this boundary, new API, automation, seed, and ENV_STORAGE_ENDPOINTS features_config values must use the canonical shape emitted by the Admin frontend. Unknown names and fields are rejected instead of being silently discarded. The database migration cannot rewrite environment values, so update those values before deploying. Invalid active values with the wrong type stop the migration explicitly. The downgrade keeps the canonical YAML because discarded inert keys and aliases cannot be reconstructed.

2026-08 canonical Storage Endpoint providers

Migration 0116_canonical_storage_endpoint_providers trims and lowercases the persisted provider values ceph, aws, and other, converts every missing or unsupported historical value to the previously effective ceph default, and adds a database constraint. New invalid values now fail validation instead of being interpreted silently as Ceph. The downgrade removes the constraint but keeps the canonicalized values.

2026-08 canonical Storage Endpoint admin endpoint

Migration 0117_remove_storage_endpoint_admin_endpoint removes the redundant storage_endpoints.admin_endpoint column. The configured override already lives in features_config at features.admin.endpoint, which is now the only persisted source. The downgrade recreates an empty nullable column but cannot reconstruct its former duplicate values.

Storage Endpoint create and update requests now reject the removed admin_endpoint property. Put the override in the canonical features_config document instead. Storage Endpoint responses expose it through features.admin.endpoint; the separate feature-detection request keeps its transient admin_endpoint probe input. Deploy migration, backend, and frontend together.

2026-08 canonical Storage Endpoint URLs

Migration 0118_canonical_storage_endpoint_urls trims persisted Storage Endpoint URLs, removes trailing slashes, and adds a database constraint that keeps new values canonical. The migration stops before changing the schema if an URL becomes empty or if two endpoints would become duplicates after normalization; repair or delete the reported rows and retry the upgrade.

Runtime Ceph Admin consumers now trust this database contract instead of repairing endpoint URLs or supplying missing provider, region, and TLS values. The downgrade removes the constraint but keeps the normalized URLs.

2026-08 Clipboard API requirement

Frontend copy actions now rely exclusively on the browser Clipboard API. The deprecated document.execCommand("copy") fallback has been removed. Serve the application from a context where navigator.clipboard.writeText is available; otherwise copy actions report that the clipboard is unavailable.

2026-08 minimal Topic contract

Manager Topic responses no longer expose the unused owner and is_ceph properties. Provider-specific listing remains an internal backend concern; clients receive only the canonical topic name, ARN, and configurable attributes. Deploy the backend and frontend together.

2026-08 minimal Portal state contract

GET /api/portal/state now returns only effective Portal role, permissions, and feature flags. The duplicated project ID, IAM identity, access-key list, provisioning marker, endpoint, usage, quota, and creation marker fields have been removed. Use the selected /api/portal/accounts entry for project identity, /api/portal/access-keys for IAM identity and keys, and /api/portal/usage for usage and quotas. Deploy the backend and frontend together.

2026-08 removal of temporary S3 connections

Migration 0106_remove_temporary_s3_connections removes the retired is_temporary, temp_user_uid, and temp_access_key_id columns. The internal temporary-connection cleanup route and the hidden execution-context contract are removed with them. Expiring STS credentials remain supported through session_token and expires_at.

The migration stops before changing the schema if a temporary connection still exists. While the previous release is running, revoke every remote RGW access key recorded by those rows and then delete the corresponding temporary connections. Verify a restorable database backup before retrying the upgrade. Deploy the migration, backend, and frontend together. Downgrade recreates empty compatibility columns but cannot restore the removed subsystem or remote keys.

2026-08 canonical execution-context catalogue

The unused GET /api/manager/accounts endpoint has been removed. The frontend already uses GET /api/me/execution-contexts?workspace=manager, whose explicit context kinds and capabilities replace the heterogeneous account-shaped response. Requests to the removed endpoint now return 404. Canonical account responses now always include a non-null Storage Endpoint ID, name, URL, and capability map, plus an explicit default-endpoint flag. Execution-context responses likewise always include endpoint name, URL, default flag, and capabilities; only the endpoint ID remains nullable for custom connections. The catalogue no longer returns quota_max_size_gb, quota_max_objects, max_buckets, max_users, max_roles, or max_groups: these fields were always null after dynamic RGW limit lookups moved to the dedicated Manager context endpoint. Deploy the backend and frontend together.

2026-08 removal of the dead Portal key setting

Migration 0093_remove_dead_portal_key_setting removes allow_portal_key from global application settings and account-level Portal overrides. The flag was persisted and exposed by the API but never affected Portal behavior.

Deploy the migration, backend, and frontend together. Portal settings payloads containing the removed field are rejected, including custom bootstrap JSON files. Invalid bootstrap files now fail explicitly instead of silently loading defaults. Downgrade does not recreate this no-op value.

2026-08 canonical Portal access-key bucket field

Portal access-key responses now expose the selected S3 bucket only as bucket_name. The duplicate response field storage_space_id has been removed; it remains the request field used when creating an external key. Deploy the backend and frontend together.

2026-08 canonical UI user roles

Migration 0092_canonical_user_roles converts users.role to exactly one of ui_superadmin, ui_admin, ui_user, or ui_none, installs a database check constraint, and sets the database default to ui_user. Known historical names are mapped to their canonical role; unknown or empty values become ui_none so the migration never widens access.

Deploy the migration, backend, and frontend together. Admin user APIs reject removed role names with 422, and the frontend no longer repairs roles from API responses or persisted sessions. Downgrade drops the constraint but does not recreate historical role spellings.

2026-08 control-plane audit boundary

Migration 0091_purge_data_plane_audit_logs irreversibly removes historical object data-plane actions and non-audit operational noise from audit_logs. Take a verified application database backup immediately before applying it; downgrade is intentionally unsupported and recovery requires restoring that backup. Deploy the migration, backend, and frontend together.

The application audit now contains only control-plane, security, configuration, and global workflow-control events. Object evidence moves to Server Access Logging or the provider's equivalent. Portal clients must use /api/portal/access-logs/page and /api/portal/access-logs/raw; the removed non-paginated /api/portal/access-logs route, /api/portal/transfers, and /api/portal/transfers/server-access-logs* routes return 404. The remaining access log routes do not accept mode and expose all S3 categories with action, space, path, identity, and result filters.

2026-08 canonical bucket migration JSON state

Migration 0090_canonical_bucket_migration_json rewrites persisted bucket migration reports, snapshots, plans, replication state, policy backups, diff samples, and event metadata to JSON objects or SQL NULL. Invalid operational state becomes NULL, forcing safe recomputation or a new precheck. Historical diff and event values remain available under value or unparsed envelopes. The absence of an original bucket policy is now stored directly as SQL NULL instead of the JSON scalar null.

Deploy the migration and backend together. Worker and API readers now reject malformed or non-object persisted JSON rather than silently treating it as missing state. The cleanup is not reversed on downgrade.

2026-08 canonical custom S3 connection endpoints

Migration 0089_canonical_s3_connection_endpoints rewrites every manual S3 connection endpoint with the exact current fields, maps the removed provider_hint key to provider, supplies current boolean defaults, and clears custom JSON from connections bound to a registered Storage Endpoint. The migration stops with the affected connection ID if a manual connection has no usable endpoint URL; repair or delete that invalid row before retrying.

Deploy the migration and backend together. Runtime readers now reject malformed, partial, or unknown custom endpoint data instead of repairing it or consulting removed model attributes. Registered endpoints also propagate their configured TLS verification flag to connections. The cleanup is not reversed on downgrade.

2026-08 canonical account Portal settings overrides

Migration 0088_canonical_portal_settings_override replaces the historical {"admin": {...}} envelope with the direct PortalSettingsOverride object. It removes obsolete portal_manager data and unknown or invalid fields while retaining every valid current override. Empty, malformed, and non-object values become NULL, the canonical representation of no account override.

Deploy the migration and backend together. The backend no longer unwraps old payloads or hides invalid persisted values, and the API rejects unknown override fields. The data cleanup is not reversed on downgrade.

2026-08 canonical audit metadata

Migration 0087_canonical_audit_metadata rewrites every non-null audit metadata payload as a JSON object. Existing objects retain their fields, other valid JSON values move under value, and malformed historical text moves under unparsed so old audit evidence is not discarded. New oversized metadata uses a bounded, valid JSON envelope with a preview and the original serialized length.

Deploy the migration and backend together. Audit and Portal readers now share a strict object contract instead of hiding malformed storage independently. The data cleanup is not reversed on downgrade.

2026-08 canonical billing operation breakdowns

Migration 0086_canonical_billing_ops_breakdown rewrites each available daily operation breakdown as a JSON object mapping operation names to integer counts. Malformed, non-object, and empty payloads become NULL, which remains the canonical representation when detailed operation data was not collected.

Deploy the migration and backend together. Billing aggregation now rejects any remaining non-canonical breakdown instead of skipping it or coercing values at runtime. The data cleanup is not reversed on downgrade.

2026-08 canonical bucket usage statistics JSON

Migration 0085_canonical_bucket_usage_stats_json rewrites every persisted bucket distribution as a JSON list of objects and every non-null warning set as a JSON list of strings. Malformed containers and entries are removed once; warnings_json = NULL remains the canonical representation of no warnings.

Deploy the migration and backend together. Snapshot loading now validates every distribution entry and warning instead of returning empty data for malformed storage. The data cleanup is not reversed on downgrade.

2026-08 canonical managed-access IAM state

Migration 0084_canonical_managed_access_iam_lists rewrites the tracked IAM groups, managed policies, and inline policy names for every managed private access as ordered, deduplicated JSON lists of non-empty strings. Malformed or non-list values become empty lists.

Deploy the migration and backend together. Saga replay and cleanup now reject any remaining malformed or non-string IAM state instead of coercing or dropping values at runtime. The data cleanup is not reversed on downgrade.

2026-08 legacy Portal application settings

Migration 0099_migrate_legacy_portal_app_settings updates every database-backed application settings payload that still uses the former Portal contract. It renames allow_portal_user_bucket_create to allow_private_storage_space_create without overwriting an existing current value, and removes the obsolete bucket_access_policy, iam_group_manager_policy, and iam_group_user_policy fields. Deployments that already applied the equivalent manual repair are left unchanged.

The migration validates all rows before writing any update. It stops explicitly when a payload or its portal section is not a JSON object, or when the legacy create setting is not a boolean. Restore or correct that source value before retrying the upgrade. The optional APP_SETTINGS_PATH bootstrap file is outside Alembic's database scope and must already use the current field names. Removed policy values and renamed settings are not reconstructed on downgrade.

2026-08 canonical UI-managed OIDC scopes

Migration 0083_canonical_oidc_provider_scopes rewrites every UI-managed OIDC scope set as a non-empty JSON list of trimmed strings. Empty, malformed, or non-list values receive the current openid, email, and profile defaults.

Deploy the migration and backend together. OIDC provider loading now rejects any remaining malformed, empty, or non-string scope set instead of repairing it at runtime. The ORM and database defaults are also aligned. Data cleanup is not reversed on downgrade.

2026-08 canonical persisted application settings

Migration 0082_canonical_app_settings_payload rewrites every database-backed application settings payload as a JSON object and replaces malformed or non-object values with {}. Valid object fields remain unchanged.

Deploy the migration and backend together. Once settings have been imported to the database, loading now uses the strict AppSettings contract and exposes invalid values instead of reverting silently to defaults. The optional disk bootstrap keeps its independent missing/invalid-file fallback. Data cleanup is not reversed on downgrade.

2026-08 canonical Portal request JSON

Migration 0081_canonical_portal_request_json rewrites every Portal request payload and non-null result as a JSON object. Malformed and non-object values become {} while an absent result remains NULL for undecided requests.

Deploy the migration and backend together. Portal request execution and API serialization now reject any remaining malformed or non-object JSON instead of repairing it at runtime. The data cleanup is not reversed on downgrade.

2026-08 canonical user notification payloads

Migration 0080_canonical_user_notification_payloads rewrites notification payloads as JSON objects, replaces null or malformed values with {}, and makes user_notifications.payload_json non-nullable with the same database default.

Deploy the migration and backend together. Notification serialization now rejects non-object or malformed payloads instead of silently returning an empty object. Downgrade makes the column nullable again but keeps canonicalized data.

2026-08 canonical user UI preferences

Migration 0079_canonical_user_ui_preferences rewrites every persisted user UI preference payload to the current theme and selected_portal_account_id contract. Malformed or invalid payloads become empty preferences, removed fields are discarded, and account identifiers are trimmed.

Deploy the migration and backend together. The backend now treats any remaining non-canonical payload as data corruption instead of repairing it during profile serialization. The data cleanup is intentionally not reversed on downgrade.

2026-08 normalized tags only

Migration 0078_remove_legacy_tags_json removes the tags_json mirrors from storage endpoints, S3 accounts, S3 users, and S3 connections. Normalized tag definitions and ordered link tables have been the only read source since their introduction; the backend now writes only that canonical model.

Deploy the migration and backend together because older instances still write the removed columns. Downgrade recreates each JSON mirror from the normalized links in stored order, but the current backend never reads or maintains it.

2026-08 canonical S3 connection capabilities

Migration 0077_canonical_s3_connection_capabilities rewrites every cached S3 connection capability profile to a JSON object containing the required boolean can_manage_iam. Malformed profiles and the removed iam_capable key resolve to the safe value false; unrelated current extension fields are retained. The database default is changed to the same canonical profile.

Deploy the migration and backend together. The backend now treats missing, malformed, or non-canonical profiles as data corruption instead of repairing them at runtime. Downgrade restores the old empty-object default but cannot recreate discarded malformed or legacy values.

2026-08 schema index reconciliation

Migration 0076_remove_redundant_provider_indexes removes the non-unique LDAP and OIDC provider_id indexes that duplicated the existing unique constraints. It does not rewrite provider data or weaken uniqueness. The SQLAlchemy metadata now also reflects the indexes already installed by earlier migrations, and the full Alembic head is checked against that metadata in the backend test suite.

2026-08 canonical S3 session capabilities

Migration 0075_canonical_s3_session_capabilities materializes a complete capability snapshot for every existing direct S3 session, replaces missing or malformed snapshots with the current safe defaults, and makes s3_sessions.capabilities non-nullable. The backend no longer repairs invalid session capability data at runtime.

Stop backend instances while applying the migration so no session is created during the table alteration. Existing direct S3 sessions remain usable with their canonicalized snapshot. Downgrade makes the column nullable again but does not recreate missing or malformed values; deploy matching code if a downgrade is unavoidable.

2026-08 timezone-aware UTC migration

Migration 0074_timezone_aware_utc_timestamps converts every persisted PostgreSQL timestamp to TIMESTAMP WITH TIME ZONE. Existing values are interpreted explicitly as UTC during the conversion; no server or session timezone is consulted. The application now rejects timezone-naive datetime writes and serializes persisted values with an explicit UTC offset.

Stop all backend instances and workers before applying the migration, because old code still writes and compares naive values. Back up the database, run Alembic through revision 0074, then deploy the backend and frontend together. On large PostgreSQL tables, plan a maintenance window: each altered table is locked while PostgreSQL rewrites or validates its timestamp columns.

SQLite has no timezone-bearing timestamp storage, so the migration does not rewrite its columns. The SQLAlchemy adapter still rejects naive application writes, stores the normalized UTC wall-clock representation, and restores an aware UTC value on every read. Back up the database, -wal, and -shm files as one consistent set before upgrading.

Downgrade converts PostgreSQL values back to timezone-naive UTC. It does not restore the old application contract; deploy compatible code at the same time if a downgrade is unavoidable.

2026-08 managed private access migration

Migration 0070_managed_private_access is DB-only. It adds s3_connections.server_managed with a false default and creates the durable managed_private_accesses saga table. Existing connections and remote RGW/IAM resources are not modified, adopted, or contacted during Alembic.

Deploy backend and frontend together so Manager does not expose a provisioning action before the specialized endpoints and immutable-credential rules are available. After upgrade, smoke-test both Create my private access branches, Profile deletion, and Retry cleanup. A cleanup_pending row is operational state, not disposable bookkeeping: retain it until the idempotent remote cleanup succeeds.

Downgrade removes only the new table and marker column. It cannot clean up remote identities or keys already created by the application; delete every server-managed private connection through the running orchestrator before downgrading.

2026-08 canonical access model migration

Historical migration boundary

This section describes the intermediate schema and API at revision 0069. The later Manager and Portal account-role split replaces that single role with two independent axes. Current payloads use manager_role and portal_role, not role; see the current access contract.

Migration 0069_canonical_account_access_roles was a breaking, DB-only migration. It replaced the previous account-association dimensions with one ordered role, removed the legacy columns in the same release, and made shared S3 connections Manager-only.

At that release, the backend API accepted and returned only the canonical role; backend and frontend had to be deployed together across that boundary.

Required deployment sequence

Rolling upgrade is prohibited because old backend instances and workers still expect the removed columns.

  1. Stop old application instances and every background worker.
  2. Create and verify a restorable database backup. For SQLite, include the database, -wal, and -shm files as one consistent set. For PostgreSQL, validate the dump by restoring it to a separate database.
  3. If the migration reports associations without useful rights, set BUCKETREEF_DB_BACKUP_VERIFIED=true only after that restore verification. Empty databases and databases without such associations do not require this override.
  4. Run Alembic through revision 0069 while the old processes remain stopped.
  5. Deploy the new backend and frontend together, then start new workers.
  6. Run python -m app.scripts.reconcile_portal_iam first in its default dry-run mode. Review the per-account summary and then rerun with --apply.
  7. Smoke-test Admin associations, shared-connection remediation, empty/private Browser, Manager Browser, Portal, direct S3 sessions, and Ceph Admin Browser.

Alembic never contacts RGW. Portal IAM reconciliation is intentionally a separate, resumable command; a partial IAM failure must not alter the canonical database role.

Data transformation

  • At revision 0069, portal_user, portal_manager, and account_administrator were the only stored association role values.
  • Root account links always become account_administrator.
  • A legacy admin flag outranks a legacy Portal role.
  • Associations that previously granted no useful right are deleted, not retained with role = NULL. No tombstone or compatibility row is written.
  • Shared connections have Browser access disabled. Those with Manager access stay ready; the others receive the stable remediation reason shared_connection_manager_access_disabled.
  • Private connection owners, credentials, activity flags, and access flags are unchanged.

The downgrade reconstructs the legacy schema and fields for surviving rows. It cannot recreate deleted no-right associations; restoring the verified backup is the only recovery path for that explicitly non-reversible data.

2026-07 Portal Storage Space access migration

Migration 0066_portal_storage_space_access_model clears the database state of existing Portal Storage Spaces before installing the strict private/team access model. It removes their grants, external-credential records, and public links. It does not contact RGW or delete storage-side resources.

  1. Back up the application database and record the spaces that must be recreated.
  2. Before upgrading, remove the old Storage Spaces and revoke their external IAM credentials through the application workflow. This must happen before Alembic because a database migration cannot revoke RGW-side credentials.
  3. Apply the database migration. Any remaining Portal database metadata is purged transactionally; unrelated public links are preserved.
  4. Recreate private spaces or re-import team spaces so bucket policies, IAM identities, and fixed Portal groups are provisioned with the strict model.

There is no runtime conversion or compatibility path for old Owner grants, shared-space owners, or editable Portal IAM policies.

2026-08 S3 connection credential owner types

Migration 0073_canonical_connection_owner_types makes connection identity metadata strict. It converts rgw_user to the canonical s3_user, trims and normalizes the supported values, clears unsupported owner types, and installs a database constraint. The only stored values are now iam_user, account_user, s3_user, or NULL.

The downgrade removes the constraint but does not recreate noncanonical values. Back up the application database before the migration if those values must be inspected or exported.

2026-08 canonical S3 user endpoints

Migration 0094_canonical_s3_user_endpoints assigns every detached S3 user to the current default Ceph endpoint, then makes s3_users.storage_endpoint_id non-nullable. Before upgrading, verify that a Ceph endpoint is marked as the default. The migration stops with an explicit error when detached users exist without such an endpoint; it does not guess another backend or retain a runtime fallback.

After this boundary, S3 user creation and import APIs require storage_endpoint_id. Existing S3 users cannot change endpoint. Deploy the backend and frontend together because the request and response contracts are both strict.

2026-08 canonical S3 account endpoints

Migration 0095_canonical_s3_account_endpoints assigns every detached S3 account to the current default Ceph endpoint, then makes s3_accounts.storage_endpoint_id non-nullable. Before upgrading, verify that a Ceph endpoint is marked as the default. The migration stops with an explicit error when detached accounts exist without such an endpoint; it does not retain a runtime fallback.

After this boundary, S3 account creation and import APIs require storage_endpoint_id, and update requests reject an explicit null endpoint. Account-specific RGW operations always use the persisted endpoint. Deploy the backend and frontend together because the request and response contracts are both strict.

2026-08 canonical secret keyrings

The backend now accepts only the keyring settings JWT_KEYS and CREDENTIAL_KEYS. Before upgrading, replace a singular FERNET_KEY with a one-item JWT_KEYS list and replace a singular CREDENTIAL_KEY with a one-item CREDENTIAL_KEYS list. Keep every historical key required to validate active JWTs or decrypt stored credentials, with the key used for new values first.

Empty keyrings are rejected at startup. The singular environment variables are ignored and no longer provide runtime compatibility.

2026-08 strict credential encryption

Migration 0098_encrypt_plaintext_secrets encrypts any remaining plaintext credentials in storage endpoints, S3 accounts, IAM users, S3 users, S3 connections, authentication providers, and S3 sessions. After this boundary, the ORM rejects plaintext or otherwise unreadable values instead of returning them as usable secrets.

Before upgrading, configure CREDENTIAL_KEYS with the current key followed by every historical credential key still needed by the database. The migration stops when a stored Fernet token cannot be decrypted, rather than encrypting an unreadable token a second time. Its downgrade keeps credentials encrypted.

2026-08 Manager Browser active context

Migration 0103_manager_browser_data_access adds the non-null allow_manager_browser_data_access flag to UI-user and UI-group associations with S3 Accounts and RGW users. The secure default is false; no existing association is enabled automatically.

Deploy the backend and frontend together. The embedded /manager/browser now uses the active Manager ctx and sends X-S3-Workspace: manager-browser on all Browser API calls. An older frontend will not send that explicit surface, while an older backend cannot enforce the new per-association permission. After deployment, verify Account, RGW-user, owned private-connection, shared- connection, and immediate-revocation scenarios. Standalone /browser, Portal, and Ceph Admin Browser execution policies are unchanged.

2026-08 canonical S3 user execution context

The execution-context API now reports managed RGW users with kind="s3_user". The obsolete legacy_user value is rejected throughout the backend and frontend; no runtime alias is retained. Context identifiers remain s3u-<id>, so persisted bucket migrations and workspace selections do not require a database migration. Deploy the backend and frontend together.

2026-08 canonical S3 user association payloads

Admin user and group writes now accept only structured s3_user_links entries. The obsolete s3_user_ids request field is rejected with 422; every link must state its s3_user_id and may explicitly enable allow_manager_browser_data_access. Responses continue to expose the association details needed by the frontend. The duplicate accounts, s3_users, and s3_connections identifier arrays are no longer returned by Admin user or group responses; consume account_links, s3_user_links, and s3_connection_details instead. The nested effective_access projection also stops duplicating canonical links and details through accounts, s3_users, s3_connections, and manager_browser_s3_users. Admin user responses expose group membership only through group_details, and group responses expose members only through user_details; the duplicate group_ids and user_ids response fields are removed. Deploy the backend and frontend together.

Admin RGW user responses now expose UI-user and UI-group associations only through user_links and group_links. The duplicate user_ids, user_details, group_ids, and group_details response fields are removed. Updates also accept only the structured link fields; sending user_ids or group_ids returns 422. Deploy the backend and frontend together.

2026-08 canonical S3 connection principal details

Admin S3 connection responses no longer duplicate user_details and group_details through user_ids and group_ids. The structured details are the only response representation. The group_ids update field remains the canonical write contract for replacing group links. Deploy the backend and frontend together.

2026-08 redundant API route removal

Four backend routes that had no frontend consumer are removed without runtime aliases:

  • GET /api/admin/stats/overview: use the canonical /api/admin/stats/summary, /api/admin/stats/storage, and /api/admin/stats/traffic resources.
  • POST /api/admin/users/{user_id}/assign-account: replace account links atomically through PUT /api/admin/users/{user_id} with account_links.
  • GET /api/connections/{connection_id}/capabilities: consume the capabilities field returned by the connection list and write resources.
  • GET /api/portal/eligibility: load /api/portal/state, which enforces the same eligibility check and returns 403 when Portal is unavailable.

Requests to the removed paths return 404. Deploy the backend and frontend together.

Admin RGW account responses and minimal summaries now expose UI-user and UI-group associations only through user_links and group_links. The duplicate user_ids and group_ids response fields are removed. Updates also accept only the structured link fields, which require an explicit role for every principal; sending user_ids, group_ids, or a null link list returns 422. Send an empty list to clear an association set. The obsolete is_s3_user discriminator is also removed because these endpoints return RGW accounts exclusively. Consumers that operate on mixed execution contexts must use the canonical kind field. The unused root_user_email and root_user_id projections are no longer returned, eliminating per-account root-link lookups. Account response models now reject unknown fields internally so obsolete projections cannot be silently reintroduced. Deploy the backend and frontend together.

2026-08 schema drift reconciliation

Migration 0105_reconcile_schema_drift converges databases created by two historical schema variants. It converts legacy text-backed storage_endpoints.latitude and storage_endpoints.longitude columns to floating-point columns, while leaving already-canonical databases unchanged. Blank coordinate strings become NULL. Non-numeric, non-finite, or out-of-range coordinates stop the migration with the affected endpoint ID and name; repair those values before retrying.

Some older databases also contain the orphan can_access_manager_bucket_usage_stats column on users or ui_groups. No application model, permission check, or API consumes that flag. The migration drops the column only when it exists and requires a tested restorable backup:

  1. Back up the database and verify a restore.
  2. Set BUCKETREEF_DB_BACKUP_VERIFIED=true for the migration process.
  3. Run the Alembic upgrade.

Fresh databases without the orphan columns do not require the backup flag. A downgrade recreates both legacy columns with false; it cannot restore discarded orphan values.

2026-08 canonical RGW account identities

Migration 0104_canonical_s3_account_identities requires every persisted RGW account to have non-empty rgw_account_id and rgw_user_uid values. The migration intentionally stops with the affected database IDs and names when an incomplete row exists; repair those identities from the authoritative Ceph RGW configuration before retrying the upgrade.

Admin RGW account responses now use the database identifier as the numeric id and expose the external RGW identifier through the required rgw_account_id field. The duplicate db_id field and the unused root rgw_user_uid projection are removed. Deploy the backend and frontend together.

Portal account responses use the same numeric database id; URL parameters and browser preferences may still serialize that value as text. Linked account details returned with UI groups now require rgw_account_id. Backend services that operate on persisted accounts consume the stored rgw_user_uid directly instead of deriving an -admin identity from the account ID.

2026-03 compatibility cleanup

Current behavior after cleanup:

  • API context selectors reject legacy account inputs (-1, null, negative ids) with 400.
  • Frontend context persistence uses the independent selectedManagerExecutionContextId and selectedBrowserExecutionContextId preferences, with ctx as the authoritative URL parameter in each tab.
  • Legacy shared local storage keys (selectedExecutionContextId, selectedS3AccountId, and selectedBrowserContextId) are ignored.
  • Removed runtime env flags BILLING_ENABLED and HEALTHCHECK_ENABLED are no longer accepted; use the explicit FEATURE_BILLING_ENABLED and FEATURE_ENDPOINT_STATUS_ENABLED force-locks.

Operator guidance

  • Remove scripts relying on legacy selector values.
  • Validate UI context persistence after upgrades.
  • Use explicit FEATURE_* controls for forced feature state.
  • Back up the application database before migrations.
  • Confirm the credential encryption key is unchanged after restore or redeploy.
  • Validate the Admin, Manager, Portal, and Browser entry routes with the intended roles after upgrade.