feat(web): prepare the fluxer.com domain migration (#2952)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-25 13:43:34 +02:00
1 parent e62ae77643
commit 6730a242db
223 files changed
+13412 -2669

No files matched your search

@@ -36,6 +36,7 @@ Missing settings use the defaults documented below. Invalid stored configuration
| gateway_rollout | [Gateway rollout configuration](#gateway-rollout-configuration-object) object | Gateway admission and dispatch tuning |
| voice_noise_suppression | [voice noise suppression configuration](#voice-noise-suppression-configuration-object) object | Client-side noise suppression rollout |
| push_service_delivery | [push service delivery configuration](#push-service-delivery-configuration-object) object | Push service delivery rollout |
| domain_migration | [domain migration configuration](#domain-migration-configuration-object) object | Web domain migration rollout |
| experiment_delivery | [experiment delivery configuration](#experiment-delivery-configuration-object) object | Cadence every client polls the experiments route on |
| registration | [registration configuration](#registration-configuration-object) object | Registration policy, issued URLs, and pending registrations |
| self_hosted | boolean | Whether the deployment runs in self-hosted mode |
@@ -138,6 +139,35 @@ The instance rollout of push service delivery.
Every field is present on read. An absent document or missing field uses the defaults above.
## Domain migration configuration object
The instance rollout that moves the official web client from its legacy origin to a new one. [Experiments](/http-api/experiments/#domain-migration-assignment-object) defines what a signed-in client resolves from it. The [instance discovery document](/http-api/instance/#domain-migration-object) publishes `enabled`, `anonymous_rollout_basis_points`, `rollout_salt`, and `standalone_forwarding` for clients that are not signed in.
### Structure
| Field | Type | Description |
| --- | --- | --- |
| enabled | boolean | Whether the rollout runs at all (default false) |
| config_version | integer | Revision counter, raised by Fluxer and never accepted from a request |
| rollout_basis_points | integer | Share of accounts the rollout selects, in basis points (0-10000, default 0) |
| rollout_salt | string | Salt of the sampling hash (1-64 printable ASCII characters, default `domain-migration-v1`) |
| included_user_ids | array[snowflake] | Accounts the rollout always selects, up to 1000 entries (default empty) |
| excluded_user_ids | array[snowflake] | Accounts the rollout never selects, up to 1000 entries (default empty) |
| anonymous_rollout_basis_points | integer | Share of logged-out devices the rollout moves, in basis points (0-10000, default 0) |
| standalone_forwarding | boolean | Whether installed desktop web apps forward to the new origin once their data has moved (default false) |
Every field is present on read. An absent document or missing field uses the defaults above.
`excluded_user_ids` is applied before `included_user_ids`, so the rollout never selects an account in both.
An installed Chromium desktop web app moves its data like a browser tab, then stays on the legacy origin and offers to install the app from the new one. Set `standalone_forwarding` once the web app manifest lists the new origin in `scope_extensions` and the new origin serves the matching association file. From then on the installed app forwards like a browser tab. Installed mobile and Safari web apps never forward, whatever the value.
Only the official web client acts on this configuration. On any other instance it changes nothing a client does.
:::caution[`enabled` is the kill switch]
Setting `enabled` to false stops new migrations and also stops forwarding from the legacy origin for clients that already moved. Excluding an account only stops a migration that has not started yet.
:::
## Experiment delivery configuration object
How often a client polls [Get experiment assignments](/http-api/experiments/#get-experiment-assignments), and how widely those polls are spread. The setting is instance-wide and applies to every experiment at once, so adding an experiment adds no second cadence to tune.
@@ -547,6 +577,7 @@ The body has one optional object for each section. Fluxer leaves an absent secti
| gateway_rollout? | object | Any subset of the [Gateway rollout configuration](#gateway-rollout-configuration-object) fields, each bound as documented there |
| voice_noise_suppression? | object | Any subset of the [noise suppression](#voice-noise-suppression-configuration-object) fields |
| push_service_delivery? | object | Any subset of the [push service delivery](#push-service-delivery-configuration-object) fields |
| domain_migration? | object | Any subset of the [domain migration](#domain-migration-configuration-object) fields |
| experiment_delivery? | object | Any subset of the [experiment delivery](#experiment-delivery-configuration-object) fields |
| registration? | object | `mode` and `admin_registration_urls_enabled` |
| app_public?<sup>2</sup> | object | `branding`, `setup`, `legal`, and `registration` sub-objects, each merged field by field |
@@ -562,6 +593,8 @@ The body has one optional object for each section. Fluxer leaves an absent secti
`push_service_delivery` works the same way, over the [push service delivery configuration](#push-service-delivery-configuration-object) fields and its own `config_version`.
`domain_migration` works the same way, over the [domain migration configuration](#domain-migration-configuration-object) fields and its own `config_version`.
`experiment_delivery` takes both [experiment delivery configuration](#experiment-delivery-configuration-object) fields, each bound as documented there. It is a section of its own, so a write to it changes no `config_version` and changes no assignment, only the cadence on which clients ask for one.
<sup>3</sup> A secret such as `klipy_api_key`, `api_key`, `hcaptcha_secret_key`, `turnstile_secret_key`, or the SMTP `password` is written when supplied and left alone when absent. `integrations.bluesky.keys` is the only way to write the Bluesky signing keys counted as `bluesky.key_count`. It takes up to 8 entries of `kid` (1-255 characters) and nullable `private_key` (up to 10000 characters), and replaces the stored key set outright
@@ -598,7 +631,7 @@ Fluxer skips URL validation while the merged configuration leaves single sign-on
| 400 | [error response](/admin-api/#error-response) | A policy transition is refused, returned as `INSTANCE_POLICY_TRANSITION_NOT_ALLOWED` |
:::caution[Sections are applied one after another]
The order is `gateway_rollout`, `voice_noise_suppression`, `push_service_delivery`, `experiment_delivery`, `sso`, `registration`, `app_public` branding, legal, and registration fields, `integrations`, `media`, `policy`, and finally `app_public.setup`. A failure part way through leaves the earlier sections written.
The order is `gateway_rollout`, `voice_noise_suppression`, `push_service_delivery`, `domain_migration`, `experiment_delivery`, `sso`, `registration`, `app_public` branding, legal, and registration fields, `integrations`, `media`, `policy`, and finally `app_public.setup`. A failure part way through leaves the earlier sections written.
:::
### Side effects