Implement Organization Maintenance Mode
## Problem statement We need to be able to make a given Organization unavailable on its source Cell while a migration (eg. to a Protocell) or isolation enablement is in progress. This is **not** the same as instance-wide Maintenance Mode: only the affected Organization is frozen, and every other Organization sharing the Cell stays fully operational. The design is formalized in [ADR 010: Organization Maintenance Mode](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/organization/decisions/010_organization_maintenance_mode/). In summary: * A per-Organization state on `Organizations::Stateful`: `active → maintenance_initialization → maintenance` (and back), with a `reason` recorded for audit (migration, isolation, incident, billing, legal). The reason is never surfaced to end users * For this first iteration **all** requests are denied, reads included. HTTP method is not a reliable proxy for "no write": some `GET`s trigger DB writes or enqueue Sidekiq jobs. Full or partial reads may be reintroduced in a future iteration * **No authentication exemption.** Sign-in, OAuth, JWT, and SAML/SSO callbacks are denied like any other request, because auth flows `INSERT` new `users` rows and update per-request timestamps that can cascade into org-owned state * Enforcement keyed off `Current.organization` at every request surface: controllers, REST API (Grape), GraphQL, `Gitlab::GitAccess`, Container Registry, LFS, and Sidekiq. Deliberately no Rack middleware * Org-scoped Sidekiq jobs already queued or in-flight **drain to completion**; cron workers skip Organizations in maintenance; BBMs are paused on the source Cell and re-enqueued on the destination Cell after cutover * A cutover readiness contract (with a drain confirmation window and bounded wait then escalate) gates the data-copy step on a fully drained source Cell * The default Organization is excluded, since freezing it would be equivalent to taking the whole instance offline The POC (gitlab-org/gitlab!228743) validated the approach and will not be merged; production implementation builds on the merged unified state machine (gitlab-org/gitlab!230909). ## Exit criteria * All requests (web UI, GraphQL queries and mutations, REST API, git pull and push, registry, LFS) to an Organization in `maintenance_initialization` or `maintenance` are denied with a structured error: `503` + `Retry-After` for time-bounded reasons, `403` otherwise * Browser requests render a static maintenance page with generic copy that does not reveal the reason or any infrastructure detail (Cell, migration) * Authentication is denied with no exemption; no new `users` rows are created for an Organization in maintenance * Org-scoped Sidekiq jobs drain to completion, cron workers skip the Organization in maintenance and its projects and namespaces, and every skip or cancel emits a structured log with `organization_id`, `worker`, and `jid` * Cell-wide cron workers that iterate org-owned data filter to active Organizations via a shared scope, with tests asserting non-`active` Organizations are not yielded * A cutover readiness check reports when the source Cell is fully drained for the Organization, confirmed over at least two runs, escalating to an operator if it does not converge * New pipelines are blocked and in-flight CI jobs are cancelled on entering maintenance * Every entry into and exit from the maintenance states emits an Organization-level audit event * The default Organization cannot be transitioned into maintenance * Rollout is gated by a single Organization-scoped derisk feature flag that is removed once fully rolled out; no permanent ops flag is added ## Status 2026-08-20 *Block-all enforcement is merged behind the flag; rollout is queued behind a rename refactor.* ## 🕐 total hours spent this week by all contributors: 30 ## 🎉 achievements * ADR merged: https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/20741+ — **Organization Maintenance Mode** is now the agreed design, blocking every request for a frozen Organization with no auth exemption. * Enforcement merged: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250225+ blocks all requests, including GETs and GraphQL reads, and serves a 503 maintenance page. Closes https://gitlab.com/gitlab-org/gitlab/-/work_items/607966+. * Both reverts of the now-redundant auth work merged: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/249739+ and https://gitlab.com/gitlab-org/gitlab/-/merge_requests/249855+. ## 🚫 blockers * Rollout is blocked until the rename refactor https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250667+ and the stacked registry cleanup https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250986+ are merged. These are blocked on reviews by the container registry team. ## ▶️ next * Merge https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250667+ and https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250986+. * Enable the flag in non-production, then verify end-to-end against an internal Organization per https://gitlab.com/gitlab-org/gitlab/-/work_items/618032+. _Copied from https://gitlab.com/groups/gitlab-org/-/epics/20404#note_3708316640_
epic