mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
1015 lines
63 KiB
Plaintext
1015 lines
63 KiB
Plaintext
---
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
title: Admin instance
|
|
description: Instance configuration, branding, registration control, limits, and diagnostics.
|
|
---
|
|
|
|
import RouteHeader from '@/components/RouteHeader.astro';
|
|
|
|
Instance configuration is everything an operator can change at runtime. It covers single sign-on, Gateway rollout, registration policy, branding and legal links, instance policy, third party integrations, premium billing, media retention, and the ordered limit configuration. The [Instance](/http-api/instance/) resource serves the subset published to unauthenticated clients.
|
|
|
|
Every write is a merge over the stored configuration, and an omitted key leaves the stored value unchanged. The limit configuration write is the one exception and replaces the stored document.
|
|
|
|
Reading configuration requires the [Admin ACL](/admin-api/#acl-registry) `instance:config:view`, and writing it requires `instance:config:update`. The limit configuration has its own pair, `instance:limit_config:view` and `instance:limit_config:update`. Pending registrations are read and decided with `registration:review` alone.
|
|
|
|
:::caution[No read returns a stored secret]
|
|
A secret is reported by a companion boolean such as `client_secret_set`. Except for the single sign-on client secret, that boolean is true whether the value is stored here or supplied by deployment configuration.
|
|
:::
|
|
|
|
## Instance configuration object
|
|
|
|
The complete runtime configuration of the deployment. Every configuration operation here except the limit configuration returns it.
|
|
|
|
Missing settings use the defaults documented below. Invalid stored configuration causes an error rather than silently resetting a policy. Operators can find recovery guidance under [stored instance policy](/operator/configuration/#stored-instance-policy).
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| sso | [SSO configuration](#sso-configuration-object) object | Single sign-on settings |
|
|
| gateway_rollout | [Gateway rollout configuration](#gateway-rollout-configuration-object) object | Gateway admission and dispatch tuning |
|
|
| push_relay | [push relay configuration](#push-relay-configuration-object) object | Operator consent to the Fluxer-run push relay |
|
|
| domain_migration | [domain migration configuration](#domain-migration-configuration-object) object | Web domain migration rollout |
|
|
| voice_p2p | [peer-to-peer voice configuration](#peer-to-peer-voice-configuration-object) object | Peer-to-peer voice rollout |
|
|
| captcha | [captcha configuration](#captcha-configuration-object) object | ALTCHA proof-of-work captcha settings |
|
|
| 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 and issued URLs |
|
|
| self_hosted | boolean | Whether the deployment runs in self-hosted mode |
|
|
| account_identity | [account identity configuration](#account-identity-configuration-object) object | How members sign in, and whether that can still change |
|
|
| app_public | [public application configuration](#public-application-configuration-object) object | Branding, legal, setup, and registration field policy |
|
|
| policy | [instance policy](#instance-policy-object) object | Community, direct message, premium, and gating policy |
|
|
| integrations | [instance integrations](#instance-integrations-object) object | Third party provider settings and their resolved availability |
|
|
| media | [instance media](#instance-media-object) object | Attachment retention settings |
|
|
| billing | [instance billing](#instance-billing-object) object | Premium billing settings and the state resolved from them |
|
|
|
|
## Account identity configuration object
|
|
|
|
How members of the deployment sign in. It is read-only here. The setup wizard chooses it through [set the setup sign-in method](/http-api/instance/#set-the-setup-sign-in-method), and [Sign-in methods](/operator/sign-in/) explains the rules.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| mode | string | The [account identity mode](/http-api/instance/#account-identity-modes) in effect, `email` or `username` |
|
|
| locked<sup>1</sup> | boolean | Whether the mode can no longer change |
|
|
| tag_style | string | How usernames are tagged, one of the [tag styles](/http-api/instance/#tag-styles) |
|
|
|
|
<sup>1</sup> True on a hosted deployment, and on a self-hosted one once setup is complete or the first account exists
|
|
|
|
## SSO configuration object
|
|
|
|
Single sign-on settings for the deployment's OpenID Connect provider.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| enabled | boolean | Whether single sign-on is offered |
|
|
| enforced<sup>1</sup> | boolean | Whether single sign-on is the only accepted login method |
|
|
| display_name | ?string | Provider name shown on the login screen |
|
|
| issuer | ?string | OpenID Connect issuer |
|
|
| authorization_url | ?string | Authorisation endpoint |
|
|
| token_url | ?string | Token endpoint |
|
|
| userinfo_url | ?string | Userinfo endpoint |
|
|
| jwks_url | ?string | JWKS endpoint |
|
|
| client_id | ?string | Registered client identifier |
|
|
| client_secret_set | boolean | Whether a client secret is stored |
|
|
| scope | ?string | Space-separated scope string |
|
|
| allowed_domains<sup>2</sup> | array[string] | Email domains permitted to sign in (max 100 entries) |
|
|
| auto_provision | boolean | Whether a first-time sign-in creates an account |
|
|
| redirect_uri<sup>3</sup> | ?string | Redirect URI to register with the provider |
|
|
|
|
<sup>1</sup> A deployment that has never stored the value reports the same value as `enabled`
|
|
|
|
<sup>2</sup> Each entry is stored lowercased and IDNA encoded, duplicates are collapsed, and an empty entry is dropped
|
|
|
|
<sup>3</sup> The configured web application endpoint followed by `/auth/sso/callback`. It is the only redirect URI the provider needs, mobile sign-in included. No operation can set it
|
|
|
|
## Gateway rollout configuration object
|
|
|
|
Admission and dispatch tuning for the Gateway cluster.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| session_rollout_percentage | number | Percentage of sessions admitted to the new Gateway (0-100, default 100) |
|
|
| session_rollout_mode | string | `modulo` or `random` (default `modulo`) |
|
|
| guild_rollout_percentage | number | Percentage of guilds admitted to the new Gateway (0-100, default 100) |
|
|
| rpc_request_timeout_ms | integer | Deadline for one Gateway RPC (1000-60000, default 10000) |
|
|
| max_concurrent_session_starts | integer | Session starts admitted at once (1-10000, default 512) |
|
|
| max_concurrent_guild_starts | integer | Guild starts admitted at once (1-10000, default 256) |
|
|
| gateway_dispatch_relay_shards | integer | Dispatch relay shard count (1-10000, default 32) |
|
|
| gateway_dispatch_relay_max_queue | integer | Dispatch relay queue ceiling (0-1000000, default 50000) |
|
|
| voice_e2ee_scope | string | `guild_feature_only` or `platform_wide` (default `guild_feature_only`) |
|
|
|
|
Every field is present on read. An absent document or missing field uses the defaults above.
|
|
|
|
Admin reads and writes name this field `rpc_request_timeout_ms`. The legacy stored name is covered in the [operator configuration reference](/operator/configuration/#stored-instance-policy).
|
|
|
|
## Push relay configuration object
|
|
|
|
The operator's consent to the push relay supplemental privacy notice.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| relay_consent_accepted | boolean | Whether the operator accepted the push relay supplemental privacy notice (default false) |
|
|
| relay_consent_accepted_at | ?string | ISO 8601 timestamp of that acceptance, or null when the notice stands unaccepted (default null) |
|
|
| relay_consent_accepted_by | ?snowflake | Account that accepted the notice, or null when the notice stands unaccepted (default null) |
|
|
|
|
Every field is present on read. An absent document or missing field uses the defaults above.
|
|
|
|
The notice covers only the Fluxer-run relay that reaches Apple and Google for the official mobile apps. A subscription whose endpoint points at another distributor, such as a self-hosted UnifiedPush server or ntfy, never reaches that relay and needs no acceptance.
|
|
|
|
## 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_country_codes | array[string] | Countries the sampled share is limited to, as unique uppercase ISO 3166-1 alpha-2 codes, up to 250 (default empty) |
|
|
| 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) |
|
|
| included_guild_ids | array[snowflake] | Guilds whose members the rollout always selects, up to 1000 entries (default empty) |
|
|
| include_premium_users | boolean | Whether the rollout always selects accounts with active premium (default false) |
|
|
| 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` wins over every other rule. Otherwise the rollout selects an account in `included_user_ids`, a member of a guild in `included_guild_ids`, or a premium account while `include_premium_users` is true, whatever `rollout_basis_points` says.
|
|
|
|
While `rollout_country_codes` is empty, `rollout_basis_points` samples every account. Once it lists a country, the sampled share only selects an account whose request comes from a listed country, and a request with an unknown country is never sampled. The list does not affect `excluded_user_ids`, `included_user_ids`, `included_guild_ids`, `include_premium_users`, or `anonymous_rollout_basis_points`.
|
|
|
|
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.
|
|
:::
|
|
|
|
## Peer-to-peer voice configuration object
|
|
|
|
The instance rollout that lets small voice calls connect their participants directly instead of through the voice servers. [Experiments](/http-api/experiments/#peer-to-peer-voice-assignment-object) defines what a client resolves from it.
|
|
|
|
### 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_country_codes | array[string] | Countries the sampled share is limited to, as unique uppercase ISO 3166-1 alpha-2 codes, up to 250 (default empty) |
|
|
| rollout_salt | string | Salt of the sampling hash (1-64 printable ASCII characters, default `voice-p2p-v1`) |
|
|
| included_user_ids | array[snowflake] | Accounts the rollout always selects, up to 1000 entries (default empty) |
|
|
| included_guild_ids | array[snowflake] | Guilds whose members the rollout always selects, up to 1000 entries (default empty) |
|
|
| include_premium_users | boolean | Whether the rollout always selects accounts with active premium (default false) |
|
|
| excluded_user_ids | array[snowflake] | Accounts the rollout never selects, up to 1000 entries (default empty) |
|
|
| max_participants | integer | Most participants a peer-to-peer call holds (2-4, default 2) |
|
|
|
|
Every field is present on read. An absent document or missing field uses the defaults above.
|
|
|
|
`excluded_user_ids` wins over every other rule. Otherwise the rollout selects an account in `included_user_ids`, a member of a guild in `included_guild_ids`, or a premium account while `include_premium_users` is true, whatever `rollout_basis_points` says.
|
|
|
|
While `rollout_country_codes` is empty, `rollout_basis_points` samples every account. Once it lists a country, the sampled share only selects an account whose request comes from a listed country, and a request with an unknown country is never sampled. The list does not affect `excluded_user_ids`, `included_user_ids`, `included_guild_ids`, or `include_premium_users`.
|
|
|
|
The rules above select who may start a peer-to-peer call. Joining a call that already runs peer-to-peer needs only `enabled`, so a partial rollout does not keep unselected accounts out of running calls.
|
|
|
|
`max_participants` applies to every account, selected or not. A join that would take a peer-to-peer call past it is refused as full, as [Peer-to-peer voice](/gateway/events/#peer-to-peer-voice) describes. Lowering it removes nobody from a running call.
|
|
|
|
:::caution[`enabled` is the kill switch]
|
|
Setting `enabled` to false makes every new call use the voice servers. A call that already runs peer-to-peer keeps running and refuses new joins until it ends or a participant switches it to the voice servers. Fluxer never moves a running call on its own.
|
|
:::
|
|
|
|
## Captcha configuration object
|
|
|
|
The ALTCHA proof-of-work check that the API issues and verifies itself on gated operations. [CAPTCHA handling](/topics/captcha/) defines the challenge exchange and lists the gated operations.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| enabled | boolean | Whether gated operations require a solved challenge (default true) |
|
|
| cost | integer | PBKDF2 iterations per solving attempt (1000-20000, default 5000) |
|
|
| max_counter | integer | Upper bound of the hidden counter a client searches for (100-20000, default 1000) |
|
|
|
|
Every field is present on read. An absent document or missing field uses the defaults above, so the check is on until an administrator sets `enabled` to false. Setting `enabled` to false turns the check off for the whole instance.
|
|
|
|
Each challenge hides its counter between half of `max_counter` and `max_counter`, and a client tries counters from 0 upward. Solve time grows with `cost` times `max_counter`. Fluxer spends one attempt at `cost` to issue each challenge.
|
|
|
|
## 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.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| poll_interval_seconds | integer | Seconds between client revalidations (60-86400, default 300) |
|
|
| poll_jitter_percent | integer | Maximum random change to each revalidation interval, as a percentage in either direction (0-50, default 15) |
|
|
|
|
Every field is present on read. An absent document or missing field uses the defaults above.
|
|
|
|
Both fields are served to every account, whether or not any experiment targets that account, and neither one is versioned by `config_version`. A client that has never received a response from the experiments route uses built-in defaults of 300 seconds and 15 per cent, which equal the defaults above. A client that cannot read the route never receives either field.
|
|
|
|
## Registration configuration object
|
|
|
|
Registration policy in force, plus every issued registration URL. Accounts awaiting a decision are read through [List pending registrations](#list-pending-registrations).
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| mode | string | [Registration mode](#registration-modes) |
|
|
| admin_registration_urls_enabled<sup>1</sup> | boolean | Whether Admin-issued registration URLs are accepted |
|
|
| urls<sup>2</sup> | array[[registration URL](#registration-url-object) object] | Every issued registration URL, including revoked and exhausted records |
|
|
|
|
<sup>1</sup> While the value is false, registration refuses every supplied registration URL code, so an issued URL stops working without being revoked
|
|
|
|
<sup>2</sup> Ordered by creation time, newest first
|
|
|
|
The URLs are embedded in the configuration response. There is no separate listing operation and no pagination, so every configuration read returns every issued URL.
|
|
|
|
## Registration modes
|
|
|
|
| Value | Description |
|
|
| --- | --- |
|
|
| open | Anyone can register |
|
|
| approval | Anyone can register, and an Admin decision is required before the account can be used |
|
|
| closed | Public registration is closed |
|
|
|
|
A valid registration URL is accepted in every mode, including `closed`, and its own `approval_required` replaces the mode for the account it creates.
|
|
|
|
## Registration URL object
|
|
|
|
A registration URL is an invitation an Admin can issue while the registration mode is `closed` or `approval`. It has its own expiry, use budget, and approval requirement.
|
|
|
|
Fluxer accepts a URL while it has no revocation time, has not passed its expiry, and has a use count below `max_uses`. A URL failing any of those tests is still reported here.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| id<sup>1</sup> | string | Identifier of the registration URL (1-128 characters) |
|
|
| label | ?string | Admin note attached to the URL, or null when unset |
|
|
| created_by_user_id | snowflake | Admin account that issued the URL |
|
|
| created_at | ISO8601 timestamp | Time the URL was issued |
|
|
| expires_at | ?ISO8601 timestamp | Time the URL stops working, or null when it never expires |
|
|
| max_uses | ?integer | Maximum permitted uses, or null for unlimited use |
|
|
| use_count | integer | How many registrations have completed through the URL |
|
|
| revoked_at | ?ISO8601 timestamp | Time the URL was revoked, or null while it has not been revoked |
|
|
| approval_required<sup>2</sup> | boolean | Whether an account created through the URL still needs an Admin decision |
|
|
| last_used_at | ?ISO8601 timestamp | Time the URL was last used, or null when it never has been |
|
|
| last_used_by_user_id | ?snowflake | Most recent account created through the URL, or null when there is none |
|
|
|
|
<sup>1</sup> A randomly generated UUID that is also the bearer code authorising registration, so every reader of the configuration can redeem an unrevoked URL
|
|
|
|
<sup>2</sup> The value replaces the instance registration mode for an account created through this URL, in both directions. A URL with the value false lets an account skip approval on an instance in `approval` mode
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"id": "3f2a91c4-6d1e-4a77-9f0b-2c5d8e114a20",
|
|
"label": "Design team",
|
|
"created_by_user_id": "1478812292088791040",
|
|
"created_at": "2026-08-20T10:00:00.000Z",
|
|
"expires_at": null,
|
|
"max_uses": 25,
|
|
"use_count": 3,
|
|
"revoked_at": null,
|
|
"approval_required": false,
|
|
"last_used_at": "2026-08-29T18:22:10.000Z",
|
|
"last_used_by_user_id": "1500901337221828608"
|
|
}
|
|
```
|
|
|
|
## Pending registration object
|
|
|
|
One account that registered and is still waiting for an Admin decision.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| user_id | snowflake | Account awaiting a decision |
|
|
| username | string | Username chosen at registration |
|
|
| discriminator | integer | Discriminator tag from 0 to 9999 |
|
|
| global_name | ?string | Display name of the account, or null when unset |
|
|
| email | ?string | Email address of the account, or null when none is stored |
|
|
| requested_at | ISO8601 timestamp | Time the account registered |
|
|
| registration_url_id | ?string | [Registration URL](#registration-url-object) the account signed up through (1-128 characters), or null when it registered directly |
|
|
| client_ip<sup>1</sup> | ?string | IP address the account registered from, or null when none was recorded |
|
|
|
|
<sup>1</sup> Stored at registration time and never refreshed, so it can be stale by the time an Admin reads it
|
|
|
|
## Pending registration list object
|
|
|
|
Every account still waiting for an Admin decision.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| pending_registrations<sup>1</sup> | array[[pending registration](#pending-registration-object) object] | Accounts awaiting a decision |
|
|
|
|
<sup>1</sup> Ordered by request time, oldest first. There is no pagination
|
|
|
|
## Public application configuration object
|
|
|
|
Branding, legal, setup and registration field policy, in the Admin form of what the [Instance](/http-api/instance/) resource publishes to clients.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| branding | [public branding](/http-api/instance/#public-branding-object) object | Product name, image URLs, and theme colour |
|
|
| setup<sup>1</sup> | object | Has the single boolean `configured` |
|
|
| legal | [public legal configuration](/http-api/instance/#public-legal-configuration-object) object | Terms, privacy, and community guidelines URLs |
|
|
| registration<sup>2</sup> | [public registration fields](/http-api/instance/#public-registration-fields-object) object | Registration field collection policy |
|
|
|
|
<sup>1</sup> The [Instance](/http-api/instance/) resource publishes `admin_url` in this object as well
|
|
|
|
<sup>2</sup> The object has the single boolean `collect_date_of_birth`
|
|
|
|
## Instance policy object
|
|
|
|
Community, direct message, premium and gating policy for the whole deployment.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| single_community_enabled | boolean | Whether the deployment presents one community |
|
|
| single_community_guild_id | ?string | Guild used as that community, or null before one has been created |
|
|
| direct_messages_disabled | boolean | Whether direct messages are disabled |
|
|
| direct_messages_locked<sup>1</sup> | boolean | Whether the direct message setting is locked against further change |
|
|
| guild_create_access | boolean | Whether every account can create communities, or only admins and holders of the `feature_guild_create` limit |
|
|
| premium_mode | string | [Premium mode](#premium-modes) |
|
|
| services | object | Operator overrides for `gif_enabled`, `youtube_enabled`, and `bluesky_enabled`. Each is nullable, and null means no override |
|
|
| services_resolved<sup>2</sup> | object | The same keys as concrete booleans, resolved from the override and the provider's own availability |
|
|
| services_available<sup>3</sup> | object | Provider availability for `gif`, `youtube`, and `bluesky`, with no operator override applied |
|
|
|
|
<sup>1</sup> The lock is set each time direct messages are re-enabled. [Update instance configuration](#update-instance-configuration) clears it when the policy object has `direct_messages_locked` set to false
|
|
|
|
<sup>2</sup> Each key is the operator override when one is set, and otherwise the matching `services_available` value
|
|
|
|
<sup>3</sup> `gif` and `youtube` report whether an API key resolves from the stored configuration or the deployment configuration. `bluesky` is the value of `integrations.bluesky.effective_enabled`
|
|
|
|
## Premium modes
|
|
|
|
| Value | Description |
|
|
| --- | --- |
|
|
| mirror | Resolve premium access from the account's own entitlement and premium flags |
|
|
| everyone | Grant premium access to every account on a self-hosted deployment |
|
|
|
|
A self-hosted deployment issues gift codes and sells premium only in `mirror`.
|
|
|
|
## Instance integrations object
|
|
|
|
Every provider reports its stored settings, a `_set` boolean in place of each secret, and the availability resolved from the stored settings and the deployment configuration.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| gif | object | `klipy_api_key_set` and `effective_available` |
|
|
| youtube | object | `api_key_set` and `effective_available` |
|
|
| email | [email integration](#email-integration-object) object | Outbound email settings |
|
|
| bluesky | [Bluesky integration](#bluesky-integration-object) object | Bluesky client settings |
|
|
|
|
## Email integration object
|
|
|
|
Outbound email settings, and the provider resolved from them.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| enabled | ?boolean | Operator override, or null for no override |
|
|
| effective_enabled | boolean | Whether outbound email is in force |
|
|
| provider | ?string | Operator override of `smtp` or `none`, or null for no override |
|
|
| effective_provider | string | Provider in use, either `smtp` or `none` |
|
|
| from_email | ?string | Envelope sender address |
|
|
| from_name | ?string | Envelope sender name, or null when none is set and mail is sent under the instance product name |
|
|
| smtp<sup>1</sup> | object | The SMTP server settings |
|
|
| disable_new_ip_authorization | boolean | Whether new-IP authorisation email is suppressed by the stored setting |
|
|
| effective_disable_new_ip_authorization<sup>2</sup> | boolean | Whether it is suppressed once outbound email state is taken into account |
|
|
|
|
<sup>1</sup> Holds `host`, `port`, `username`, `tls_mode`, `effective_tls_mode`, and `password_set`. `host` and `username` are nullable, `port` is a nullable integer from 1 to 65535, and `password_set` is a plain boolean. `tls_mode` is the saved [SMTP TLS mode](#smtp-tls-mode), or null when none is saved. `effective_tls_mode` is the mode outgoing email uses, or null while the provider is not `smtp`
|
|
|
|
<sup>2</sup> True whenever the stored setting is true or outbound email is not in force
|
|
|
|
On a deployment whose account identity mode is `username`, `effective_enabled` is always false whatever the stored settings hold.
|
|
|
|
## Bluesky integration object
|
|
|
|
Client identity the deployment presents to Bluesky, and the number of signing keys in its effective configuration.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| enabled | ?boolean | Operator override, or null for no override |
|
|
| effective_enabled | boolean | Whether the integration is enabled with at least one configured signing key |
|
|
| client_name | ?string | Client name presented to Bluesky |
|
|
| client_uri | ?string | Client URI |
|
|
| logo_uri | ?string | Client logo URI |
|
|
| tos_uri | ?string | Client terms URI |
|
|
| policy_uri | ?string | Client policy URI |
|
|
| key_count | integer | Number of configured signing keys |
|
|
|
|
Signing keys are write-only and must have unique identifiers.
|
|
|
|
## Instance billing object
|
|
|
|
Stripe billing for the premium tier. Each stored field is nullable, and null falls back to the [deployment configuration](/operator/configuration/#payments).
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| enabled | ?boolean | Operator override of the billing switch, or null for no override |
|
|
| effective_enabled | boolean | Whether billing is switched on once the override and the deployment configuration are combined |
|
|
| stripe_secret_key_set | boolean | Whether a Stripe secret key is stored or supplied by deployment configuration |
|
|
| stripe_webhook_secret_set | boolean | Whether a Stripe webhook secret is stored or supplied by deployment configuration |
|
|
| stripe_secret_key_stored | boolean | Whether a Stripe secret key is stored, not counting deployment configuration |
|
|
| stripe_webhook_secret_stored | boolean | Whether a Stripe webhook secret is stored, not counting deployment configuration |
|
|
| automatic_tax<sup>4</sup> | ?boolean | Stored switch for Stripe automatic tax at checkout, or null for the default |
|
|
| tax_id_collection<sup>4</sup> | ?boolean | Stored switch for collecting a buyer tax ID at checkout, or null for the default |
|
|
| terms_consent_required<sup>4</sup> | ?boolean | Stored switch for requiring terms of service consent at checkout, or null for the default |
|
|
| effective_automatic_tax | boolean | Whether checkout uses Stripe automatic tax |
|
|
| effective_tax_id_collection | boolean | Whether checkout collects a buyer tax ID |
|
|
| effective_terms_consent_required | boolean | Whether checkout requires terms of service consent |
|
|
| default_currency | ?string | Currency used when no country mapping applies, or null |
|
|
| prices<sup>1</sup> | ?map[string, [billing price set](#billing-price-set-object) object] | Stored price sets keyed by currency, or null to use the deployment configuration |
|
|
| country_currencies | ?object | Stored map of two-letter country codes to currencies, or null |
|
|
| legacy_prices<sup>2</sup> | ?object | Stored map of legacy price slots to arrays of Stripe price IDs, or null to use the deployment configuration |
|
|
| billing_active<sup>3</sup> | boolean | Whether premium purchases are available |
|
|
| stripe_serviceable<sup>5</sup> | boolean | Whether existing Stripe subscriptions can be managed, cancelled and billed |
|
|
| catalog_mode | string | `operator` while `prices` is stored, otherwise `env` |
|
|
| webhook_url | string | The URL to register as the Stripe webhook endpoint |
|
|
|
|
<sup>1</sup> Stored prices replace the deployment catalogue entirely and take any three-letter currency code. A buyer gets the currency mapped to their country, then `default_currency`, then the first configured currency. Fluxer's country checks for localised currencies do not apply to them
|
|
|
|
<sup>2</sup> A slot is `monthly`, `yearly`, `gift_1_month`, or `gift_1_year`, an underscore, and a currency, such as `monthly_GBP`. Existing subscriptions on a listed price keep renewing
|
|
|
|
<sup>3</sup> True when billing is switched on, a Stripe secret key is set, at least one currency has both a monthly and a yearly price, and premium tiering is in force. Premium tiering is always in force on a hosted deployment, and on a self-hosted one only in the `mirror` [premium mode](#premium-modes)
|
|
|
|
<sup>4</sup> A hosted deployment always turns all three on. A self-hosted deployment defaults all three to off. Automatic tax needs Stripe Tax set up, and terms consent needs a terms of service URL in the Stripe public details
|
|
|
|
<sup>5</sup> True while a Stripe secret key is set and premium tiering is in force. On a hosted deployment billing must also be switched on. Only the checkout routes follow `billing_active`
|
|
|
|
## Billing price set object
|
|
|
|
The Stripe price IDs for one currency. Each is nullable.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| monthly | ?string | Price ID of the monthly recurring plan |
|
|
| yearly | ?string | Price ID of the yearly recurring plan |
|
|
| gift_1_month | ?string | Price ID of the one-month gift |
|
|
| gift_1_year | ?string | Price ID of the one-year gift |
|
|
|
|
## Instance media object
|
|
|
|
Attachment retention overrides the operator has set, and the values in force.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| attachment_decay<sup>1</sup> | object | Nullable operator overrides plus an `effective` object of the same keys with concrete values |
|
|
|
|
<sup>1</sup> The override keys are `enabled`, `min_size_mb`, `max_size_mb`, `max_eligible_size_mb`, `min_lifetime_days`, `max_lifetime_days`, `curve`, `renew_threshold_days`, and `renew_window_days`. Each is null when the deployment default applies, and `effective` reports the value in force. `enabled` is a boolean, `curve` is a number from 0 to 1, the `_mb` keys are positive numbers, and the `_days` keys are positive safe integers
|
|
|
|
Fluxer clears `max_size_mb` when it is not above `min_size_mb`, clears `max_eligible_size_mb` when it is below `max_size_mb`, and clears `max_lifetime_days` when it is below `min_lifetime_days`. A cleared key uses the deployment default. The effective size range must have a finite maximum above its minimum, and retention must produce a valid expiry date. Check the returned `effective` values after an update.
|
|
|
|
## Branding asset kinds
|
|
|
|
| Value | Description |
|
|
| --- | --- |
|
|
| icon | Writes `branding.icon_url` |
|
|
| symbol | Writes `branding.symbol_url` |
|
|
| logo | Writes `branding.logo_url` |
|
|
| wordmark | Writes `branding.wordmark_url` |
|
|
| favicon | Writes `branding.favicon_url` |
|
|
|
|
## SMTP TLS mode
|
|
|
|
How the connection to the SMTP server is encrypted.
|
|
|
|
| Value | Description |
|
|
| --- | --- |
|
|
| implicit | TLS from the first byte, usually on port 465 |
|
|
| starttls | A plain connection that must upgrade with STARTTLS, usually on port 587 |
|
|
| opportunistic | A plain connection that upgrades with STARTTLS when the server offers it |
|
|
| none | No TLS, even when the server offers STARTTLS |
|
|
|
|
With no mode saved, Fluxer follows `FLUXER_EMAIL_SMTP_TLS_MODE`, then the older `FLUXER_EMAIL_SMTP_SECURE`. With neither set it uses `implicit` on port 465 and `starttls` on every other port.
|
|
|
|
## SMTP test result object
|
|
|
|
Whether one [Create SMTP test](#create-smtp-test) connection attempt succeeded, and the failure text when it did not.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| ok | boolean | Whether the server accepted the connection and credentials |
|
|
| error<sup>1</sup> | ?string | Failure text, or null when the test succeeded |
|
|
|
|
<sup>1</sup> The message reported by the SMTP client, so it can repeat text supplied by the remote server
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"ok": false,
|
|
"error": "Invalid login: 535 5.7.8 Authentication credentials invalid"
|
|
}
|
|
```
|
|
|
|
## Registration URL creation object
|
|
|
|
One issued registration URL together with the link an Admin hands out.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| registration_url | [registration URL](#registration-url-object) object | Record that was issued |
|
|
| code<sup>1</sup> | string | Bearer code that authorises registration (1-256 characters) |
|
|
| url<sup>2</sup> | string | Complete registration link built from the configured web application endpoint |
|
|
|
|
<sup>1</sup> Equal to `registration_url.id`, so it can be recovered from any later configuration read
|
|
|
|
<sup>2</sup> The configured web application endpoint followed by `/register?registration_url=` and the percent-encoded code
|
|
|
|
## Limit configuration response object
|
|
|
|
The effective limit configuration together with the deployment defaults and the metadata an editor needs.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| limit_config | [limit configuration](#limit-configuration-object) object | Effective configuration on the serving node |
|
|
| limit_config_json | string | The same document rendered as JSON indented by two spaces, for an editor to display |
|
|
| self_hosted | boolean | Whether the deployment runs in self-hosted mode |
|
|
| defaults<sup>1</sup> | map[string, map[string, integer]] | Deployment default limits, keyed by rule identifier and then by limit key |
|
|
| metadata | map[string, [limit key metadata](#limit-key-metadata-object) object] | Display metadata for each limit key |
|
|
| categories<sup>2</sup> | map[string, string] | Display label for each metadata category |
|
|
| limit_keys | array[string] | Every [limit key](/http-api/instance/#limit-keys) in registry order |
|
|
| bounds?<sup>3</sup> | map[string, object] | Optional `min` and `max` pair for each limit key |
|
|
|
|
<sup>1</sup> Built for the deployment and the premium mode currently loaded by the serving node. Self-hosted `mirror` mode includes the `premium` rule
|
|
|
|
<sup>2</sup> The keys are `messages`, `guilds`, `channels`, `expressions`, `files`, `social`, and `features`
|
|
|
|
<sup>3</sup> Absent from every response
|
|
|
|
## Limit configuration object
|
|
|
|
Trait definitions and the ordered rules that decide each [limit key](/http-api/instance/#limit-keys).
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| traitDefinitions | array[string] | Trait names a rule filter can match |
|
|
| rules | array[[limit rule](#limit-rule-object) object] | Ordered limit rules |
|
|
|
|
The field names are camelCase, unlike the rest of the Admin API.
|
|
|
|
## Limit rule object
|
|
|
|
One rule in that ordered set, with the filters that scope it and the limits it sets.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| id | string | Rule identifier (at least 1 character) |
|
|
| filters? | object | Optional `traits` and `guildFeatures` string arrays that scope the rule |
|
|
| limits | map[string, integer] | Non-negative value for each [limit key](/http-api/instance/#limit-keys) the rule sets |
|
|
| modifiedFields?<sup>1</sup> | array[string] | Limit keys whose value differs from the deployment default |
|
|
|
|
<sup>1</sup> Compared with the deployment default rule of the same identifier, or with the deployment's `default` rule for a custom identifier. A set key absent from that default counts as modified. A rule with no differing key omits the field. Computing this field changes no value in `limits`
|
|
|
|
## Limit key metadata object
|
|
|
|
Display metadata for one [limit key](/http-api/instance/#limit-keys), which an editor uses to render its control.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| key | string | The [limit key](/http-api/instance/#limit-keys) |
|
|
| label | string | Display label |
|
|
| description | string | Description of what the limit bounds |
|
|
| category | string | Category key, resolved against `categories` |
|
|
| scope | string | `user`, `guild`, or `both` |
|
|
| isToggle | boolean | Whether the key is a feature gate whose value is 0 or 1 |
|
|
| unit? | string | `bytes` or `count` |
|
|
| min? | number | Suggested minimum for an editor |
|
|
| max? | number | Suggested maximum for an editor |
|
|
|
|
## Get instance configuration
|
|
|
|
<RouteHeader method="GET" path="/v1/admin/instance/config" auditReason />
|
|
|
|
Returns the [instance configuration](#instance-configuration-object) object. Requires `instance:config:view`.
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [instance configuration](#instance-configuration-object) object | Configuration was returned |
|
|
|
|
### Side effects
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_instance_config`, target type `instance_config`, target ID `0`, and metadata key `registration_url_count`.
|
|
|
|
### Rate limit
|
|
|
|
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
|
|
|
|
## Update instance configuration
|
|
|
|
<RouteHeader method="PATCH" path="/v1/admin/instance/config" auditReason />
|
|
|
|
Applies a merge patch to the stored configuration and returns the resulting [instance configuration](#instance-configuration-object) object. Requires `instance:config:update`.
|
|
|
|
:::note[Every section is optional and merged independently]
|
|
The body has one optional object for each section. Fluxer leaves an absent section alone. Within a section, an absent key keeps its stored value, and a key sent as null clears the stored value where the schema permits null.
|
|
:::
|
|
|
|
### JSON body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| sso?<sup>1</sup> | object | Every [SSO configuration](#sso-configuration-object) field except `client_secret_set` and `redirect_uri`, plus `client_secret` |
|
|
| gateway_rollout? | object | Any subset of the [Gateway rollout configuration](#gateway-rollout-configuration-object) fields, each bound as documented there |
|
|
| push_relay? | object | `relay_consent_accepted` from the [push relay configuration](#push-relay-configuration-object) |
|
|
| domain_migration? | object | Any subset of the [domain migration](#domain-migration-configuration-object) fields |
|
|
| voice_p2p? | object | Any subset of the [peer-to-peer voice](#peer-to-peer-voice-configuration-object) fields |
|
|
| captcha? | object | Any subset of the [captcha configuration](#captcha-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 |
|
|
| integrations?<sup>3</sup> | object | `gif`, `youtube`, `email`, and `bluesky` sub-objects, the last of which also has the `keys` array |
|
|
| media? | object | `attachment_decay` overrides, each nullable to restore the deployment default |
|
|
| policy? | [instance policy update](#instance-policy-update-structure) object | Community, direct message, premium, and gating policy |
|
|
| billing?<sup>4</sup> | object | Stored [instance billing](#instance-billing-object) fields, with `stripe_secret_key` and `stripe_webhook_secret` for the secrets |
|
|
|
|
<sup>1</sup> The supplied fields are merged over the stored configuration and the result is validated as a whole. Every supplied endpoint URL uses `https`, has no credentials and no fragment, and resolves to a publicly routable address, otherwise the request returns 400 `INVALID_FORM_BODY` with `INVALID_URL_FORMAT` or `URL_NOT_PUBLICLY_ROUTABLE`
|
|
|
|
<sup>2</sup> `branding.product_name` is 1 to 80 characters, every branding and legal URL is at most 2048 characters and nullable, `branding.theme_color` is a hex colour (`#rgb` or `#rrggbb`, the `#` is optional) and nullable, and `setup.configured` and `registration.collect_date_of_birth` are booleans. The theme colour is returned as lowercase `#rrggbb`, and pure black is treated as unset and returned as null. `branding.premium_product_name` is 1 to 40 characters, and null restores the default. `branding.premium_info_url` and `legal.guidelines_url` are absolute `http` or `https` URLs. Every string is trimmed before it is stored, and null or an empty string clears a legal URL
|
|
|
|
`push_relay` takes only `relay_consent_accepted`. Fluxer sets `relay_consent_accepted_at` and `relay_consent_accepted_by` itself on the request that changes that flag, and accepts neither from a request. Turning the flag on stamps the current time and the acting account, and turning it off clears both to null. A request that repeats the flag it already holds leaves the stamp alone.
|
|
|
|
`domain_migration` takes every [domain migration configuration](#domain-migration-configuration-object) field except `config_version`, each bound as documented there. Fluxer raises `config_version` by one on each request that supplies at least one of them. A section that is absent, or present with no field set, writes nothing and leaves `config_version` alone.
|
|
|
|
`voice_p2p` works the same way, over the [peer-to-peer voice configuration](#peer-to-peer-voice-configuration-object) fields and its own `config_version`.
|
|
|
|
`captcha` takes any subset of the [captcha configuration](#captcha-configuration-object) fields, each bound as documented there. Fluxer merges the supplied fields over the stored ones. The section has no `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`, or the SMTP `password` is written when supplied and left alone when absent. `integrations.email.smtp` takes `host`, `port`, `username`, `password`, and `tls_mode`, an [SMTP TLS mode](#smtp-tls-mode), and null clears a saved value so the environment applies again. The older boolean `secure` is still read when `tls_mode` is absent, with true as `implicit` and false as `opportunistic`. `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
|
|
|
|
<sup>4</sup> Accepted only on a self-hosted deployment. A hosted deployment rejects the section, and `app_public.branding.premium_product_name` and `premium_info_url`, with 400 `INVALID_FORM_BODY`. A Stripe secret, `enabled`, `automatic_tax`, `tax_id_collection` and `terms_consent_required` are written when supplied, cleared by null, and left alone when absent. A null `enabled` follows `FLUXER_STRIPE_ENABLED`. `prices`, `country_currencies`, and `legacy_prices` replace the stored value outright, a price slot left out is stored as null, and an empty `prices` object restores the deployment catalogue. A price ID is `price_` followed by letters and digits. A currency is three upper-case letters and a country code is two
|
|
|
|
:::note[Single sign-on URL validation is conditional]
|
|
Fluxer skips URL validation while the merged configuration leaves single sign-on disabled. A configuration both enabled and enforced fails validation against `sso` with `SSO_MISCONFIGURED` unless it resolves an authorisation endpoint, a token endpoint, a client identifier, and a claims source, which is `jwks_url` or `userinfo_url`. A set `issuer` meets the endpoint and claims source requirements, because Fluxer can discover them from the issuer.
|
|
:::
|
|
|
|
#### Instance policy update structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| single_community_enabled?<sup>1</sup> | boolean | Whether the deployment presents one community |
|
|
| single_community_name?<sup>1</sup> | string | Name used only when the community has to be created (1-100 characters) |
|
|
| direct_messages_disabled?<sup>2</sup> | boolean | Whether direct messages are disabled |
|
|
| direct_messages_locked?<sup>2</sup> | boolean | Whether the direct message setting stays locked |
|
|
| guild_create_access? | boolean | Whether every account can create communities |
|
|
| premium_mode? | string | [Premium mode](#premium-modes) |
|
|
| services? | object | Nullable `gif_enabled`, `youtube_enabled`, and `bluesky_enabled` overrides |
|
|
|
|
<sup>1</sup> Setting `single_community_enabled` to true adopts the already designated guild when one still exists. When none is designated or the designated guild was deleted, it creates a community using `single_community_name` or the configured product name. When the stored guild ID is not a valid ID, or the guild lookup fails for a reason other than an unknown guild, the operation fails and Fluxer creates no community. On a deployment whose setup is already complete, enabling it while no guild is designated fails with 400 `INSTANCE_POLICY_TRANSITION_NOT_ALLOWED`, as does enabling it when the acting Admin account cannot be resolved. Setting it to false only clears the flag and leaves the guild in place
|
|
|
|
<sup>2</sup> The setting can be changed only while `direct_messages_locked` is false, and a change attempted after the lock is set fails with 400 `INSTANCE_POLICY_TRANSITION_NOT_ALLOWED` unless the same request sets `direct_messages_locked` to false. Re-enabling direct messages sets the lock again
|
|
|
|
`direct_messages_locked` accepts only false, and a body that sets it to true fails with 400 `INVALID_FORM_BODY`.
|
|
|
|
On a self-hosted deployment, billing and the `everyone` premium mode exclude each other. A body that would leave billing switched on in `everyone` fails with 400 `INVALID_FORM_BODY` and writes nothing, whether it enables billing or switches the premium mode.
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [instance configuration](#instance-configuration-object) object | The patch was applied |
|
|
| 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`, `push_relay`, `domain_migration`, `voice_p2p`, `captcha`, `experiment_delivery`, `sso`, `registration`, `app_public` branding, legal, and registration fields, `integrations`, `media`, `policy`, `billing`, and finally `app_public.setup`. A failure part way through leaves the earlier sections written.
|
|
:::
|
|
|
|
### Side effects
|
|
|
|
Fluxer publishes a `gateway_rollout` change to the Gateway cluster. A `push_relay` change reaches the push service without a restart. A `billing` change applies without a restart. Premium mode changes affect the limits in force without replacing the saved limit configuration. On self-hosted deployments, `everyone` hides premium-filtered rules. Switching back to `mirror` restores them unless an Admin has replaced the limit configuration in the meantime. Enabling single community mode creates the community when none is designated, with the acting Admin as owner.
|
|
|
|
An update that sets `app_public.setup.configured` to true also marks the deployment as bootstrapped, so no later registration receives the wildcard Admin ACL.
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_instance_config`, target type `instance_config`, and target ID `0`. The metadata key `sections` lists the names of the non-null top-level sections in the body, comma-separated in alphabetical order, and is absent when the body supplies none. No value from the body is recorded. A request that fails part way records no entry.
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
|
|
|
## Create branding asset
|
|
|
|
<RouteHeader method="POST" path="/v1/admin/instance/config/branding-assets" auditReason />
|
|
|
|
Uploads an image, stores its [Media Proxy](/media-proxy/overview/) URL in the selected branding slot, and returns the resulting [instance configuration](#instance-configuration-object) object. Requires `instance:config:update`.
|
|
|
|
### JSON body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| kind | string | [Branding asset kind](#branding-asset-kinds) naming the slot to write |
|
|
| image?<sup>1</sup> <sup>2</sup> | ?string | Base64 image or data URI (max 16000000 characters) |
|
|
|
|
<sup>1</sup> Omitting the key and sending null both clear the slot
|
|
|
|
<sup>2</sup> The image decodes to no more than the resolved `avatar_max_size` limit, which defaults to 10485760 bytes, and is a PNG, JPEG, WebP, GIF, APNG, AVIF, HEIC, HEIF, JXL, or SVG. An animated AVIF is refused
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [instance configuration](#instance-configuration-object) object | The slot was written or cleared |
|
|
| 400<sup>1</sup> | [error response](/admin-api/#error-response) | The image cannot be decoded, exceeds the size ceiling, or is not an accepted format |
|
|
|
|
<sup>1</sup> Returned as 400 `INVALID_FORM_BODY` with `IMAGE_SIZE_EXCEEDS_LIMIT`, `INVALID_IMAGE_FORMAT`, or `FAILED_TO_UPLOAD_IMAGE` against `image`
|
|
|
|
### Side effects
|
|
|
|
The stored image is served through the Media Proxy. Non-JPEG images are re-encoded to strip metadata and JPEG images are re-encoded at quality 100. The previously referenced image is not deleted.
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `upload_branding_asset`, target type `instance_config`, target ID `0`, and metadata keys `kind` and `cleared`. `cleared` is `true` when the request cleared the slot.
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
|
|
|
## Create SMTP test
|
|
|
|
<RouteHeader method="POST" path="/v1/admin/instance/config/smtp-tests" auditReason />
|
|
|
|
Opens a connection to an SMTP server, authenticates against it, and reports the outcome as an [SMTP test result](#smtp-test-result-object) object without storing anything. Requires `instance:config:update`.
|
|
|
|
:::note[A rejected credential is still a successful test]
|
|
A test that runs to completion answers 200 whether or not the server accepted the connection. The `ok` field is the verdict and `error` is the failure text.
|
|
:::
|
|
|
|
### JSON body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| host? | ?string | SMTP host to connect to (1-255 characters after trimming) |
|
|
| port? | ?integer | SMTP port to connect to (1-65535) |
|
|
| username? | ?string | SMTP username to authenticate with (1-320 characters after trimming) |
|
|
| password? | ?string | SMTP password to authenticate with (1-4096 characters after trimming) |
|
|
| tls_mode?<sup>1</sup> | ?string | [SMTP TLS mode](#smtp-tls-mode) to connect with |
|
|
|
|
Every field left out or sent as null is filled from the saved configuration, the one outgoing email uses. The saved password is filled in only while `host` and `username` are left out or match the saved ones. A test that names another host or username without a `password` fails before any connection is made.
|
|
|
|
An empty body tests exactly what a send would use. It fails without connecting while outgoing email is not in force, which covers email turned off, a provider other than `smtp`, a missing from address, a missing host, username or password, and an instance that signs in with usernames. A test with fields in the body fails the same way when the host, username or password ends up empty.
|
|
|
|
<sup>1</sup> Left out, the saved mode applies. Sent as null, the mode follows `FLUXER_EMAIL_SMTP_TLS_MODE`, then the port
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [SMTP test result](#smtp-test-result-object) object | The test ran to completion, including a rejected credential |
|
|
|
|
### Side effects
|
|
|
|
No configuration is written and the supplied credentials are discarded when the request completes. The connection, greeting, and socket each have a ten-second deadline.
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `test_smtp_connection`, target type `instance_config`, target ID `0`, and metadata keys `port`, `tls_mode`, and `ok`. The host, username, password, and failure text are not recorded.
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
|
|
|
## Create registration URL
|
|
|
|
<RouteHeader method="POST" path="/v1/admin/instance/registration-urls" auditReason />
|
|
|
|
Issues a registration URL an Admin can hand out while the registration mode is `closed` or `approval`, and returns a [registration URL creation](#registration-url-creation-object) object. Requires `instance:config:update`.
|
|
|
|
:::caution[The code is the identifier]
|
|
The returned `code` is the same value as `registration_url.id`, and every [Get instance configuration](#get-instance-configuration) response has that identifier. Treat `instance:config:view` as a registration capability on a deployment in `closed` or `approval` mode.
|
|
:::
|
|
|
|
### JSON body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| label?<sup>1</sup> | ?string | Admin note to attach to the URL (1-120 characters), or null for none |
|
|
| expires_at? | ?ISO8601 timestamp | Time the URL stops working, or null for a URL that never expires |
|
|
| max_uses? | ?integer | Maximum permitted uses (1-1000000), or null for unlimited use |
|
|
| approval_required? | boolean | Whether an account created through the URL still needs an Admin decision (default false) |
|
|
|
|
<sup>1</sup> The label is trimmed before it is length checked, so a whitespace-only label fails body validation. Omit the key or send null for a URL with no label
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [registration URL creation](#registration-url-creation-object) object | The URL was issued |
|
|
|
|
### Side effects
|
|
|
|
A newly issued URL has a use count of zero, no revocation time, and the acting Admin as its issuer. It is the first entry of `registration.urls` on the next configuration read. The operation changes no account.
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `create_registration_url`, target type `registration_url`, target ID `0`, and metadata key `approval_required`, plus `max_uses` when the body sets it. The label is not recorded. The entry has no registration URL identifier, because the identifier is the bearer code.
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
|
|
|
## Revoke registration URL
|
|
|
|
<RouteHeader method="DELETE" path="/v1/admin/instance/registration-urls/{registration_url_id}" auditReason />
|
|
|
|
Revokes one registration URL so it can no longer be redeemed and returns the resulting [instance configuration](#instance-configuration-object) object. Requires `instance:config:update`.
|
|
|
|
### Path parameters
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| registration_url_id | string | Identifier of the registration URL to revoke (1-128 characters) |
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200<sup>1</sup> | [instance configuration](#instance-configuration-object) object | The revocation was applied |
|
|
|
|
<sup>1</sup> An identifier naming no registration URL answers 200 with the configuration unchanged. Revoking an already revoked URL keeps the original revocation time
|
|
|
|
:::caution[Revocation stops only later registrations]
|
|
An account that registered through the URL keeps its access, membership, and decision. A revocation cannot be reversed.
|
|
:::
|
|
|
|
### Side effects
|
|
|
|
The URL records its revocation time and stays in the configuration response afterwards. The operation changes no account.
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `revoke_registration_url`, target type `registration_url`, target ID `0`, and no metadata, including for an identifier that names no registration URL. The entry has no registration URL identifier, because the identifier is the bearer code.
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
|
|
|
## List pending registrations
|
|
|
|
<RouteHeader method="GET" path="/v1/admin/instance/pending-registrations" auditReason />
|
|
|
|
Returns the [pending registration list](#pending-registration-list-object) object. Requires `registration:review`.
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [pending registration list](#pending-registration-list-object) object | Pending registrations were returned |
|
|
|
|
### Side effects
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_pending_registrations`, target type `instance_config`, target ID `0`, and metadata key `pending_registration_count`.
|
|
|
|
### Rate limit
|
|
|
|
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
|
|
|
|
## Update pending registration
|
|
|
|
<RouteHeader method="PATCH" path="/v1/admin/instance/pending-registrations/{user_id}" auditReason />
|
|
|
|
Approves or rejects one pending registration and returns the resulting [pending registration list](#pending-registration-list-object) object. Requires `registration:review`.
|
|
|
|
### Path parameters
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| user_id | snowflake | Account awaiting a decision |
|
|
|
|
### JSON body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| status | string | Either `approved` or `rejected` |
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200<sup>1</sup> | [pending registration list](#pending-registration-list-object) object | The decision was applied |
|
|
|
|
<sup>1</sup> An account that no longer exists still answers 200. The pending registration is removed and no trait is written
|
|
|
|
:::caution[Decisions apply even without a pending entry]
|
|
Both decisions remove the pending registration, but the endpoint does not require the account to be in that list. Rejecting writes the `registration_rejected` trait. A later approval can clear it again. The trait can also be changed through [Set user traits](/admin-api/users/#set-user-traits).
|
|
:::
|
|
|
|
### Side effects
|
|
|
|
Approval removes both the `registration_pending_approval` trait and the `registration_rejected` trait, and joins the account to the single community when that mode is enabled and a guild is designated. A join that fails is logged and does not fail the request. Rejection removes the `registration_pending_approval` trait and adds the `registration_rejected` trait, which blocks login and every later session creation. A session issued before the decision stays valid. The pending registration is removed either way.
|
|
|
|
When the stored pending registration list fails validation, the request fails before Fluxer changes the account. A failure after Fluxer writes the account traits leaves those traits written and can leave the entry in the pending list. Check the account traits and the pending list before retrying.
|
|
|
|
One Admin audit entry with the action `approve_registration` or `reject_registration` targets the account and records the audit reason. It has no metadata, except for an account that no longer exists, whose entry has the metadata key `account_found` set to `false`.
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
|
|
|
## Get limit configuration
|
|
|
|
<RouteHeader method="GET" path="/v1/admin/limit-config" auditReason />
|
|
|
|
Returns the [limit configuration response](#limit-configuration-response-object) object. Requires `instance:limit_config:view`.
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [limit configuration response](#limit-configuration-response-object) object | The configuration was returned |
|
|
|
|
### Side effects
|
|
|
|
The response reflects the configuration in force on the node that serves the request, so two nodes can report different values while a change propagates.
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_limit_config`, target type `limit_config`, target ID `0`, and metadata key `rule_count`.
|
|
|
|
### Rate limit
|
|
|
|
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
|
|
|
|
## Replace limit configuration
|
|
|
|
<RouteHeader method="PUT" path="/v1/admin/limit-config" auditReason />
|
|
|
|
Replaces the stored limit configuration with the supplied document and returns the resulting [limit configuration response](#limit-configuration-response-object) object. Requires `instance:limit_config:update`.
|
|
|
|
:::caution[An absent custom rule is removed]
|
|
A default rule that the body omits is restored from the deployment defaults. Fluxer re-merges the write with the current defaults immediately.
|
|
:::
|
|
|
|
:::note[The stored document can differ from the body]
|
|
The re-merge restores every default rule and back-fills every default limit key the submitted rule did not override. Read the response to see what was stored.
|
|
:::
|
|
|
|
### JSON body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| limit_config<sup>1</sup> | [limit configuration](#limit-configuration-object) object | Complete replacement document |
|
|
|
|
<sup>1</sup> `traitDefinitions` can be omitted and is then stored as an empty array. Every key inside a rule's `limits` map is a known [limit key](/http-api/instance/#limit-keys) and every value is a non-negative safe integer, otherwise the body fails validation. `modifiedFields` is not accepted on input and is recomputed
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [limit configuration response](#limit-configuration-response-object) object | The configuration was replaced |
|
|
| 400 | [error response](/admin-api/#error-response) | Body validation fails, including an unknown limit key, returned as `INVALID_FORM_BODY` |
|
|
|
|
### Side effects
|
|
|
|
On a self-hosted deployment whose premium mode is `everyone`, Fluxer drops the `premium` trait definition and every rule filtered on the `premium` trait before storing the document.
|
|
|
|
Each rule's `modifiedFields` is then recomputed and the merged document replaces the stored one. Every API node adopts the new configuration. Clients observe it through the published [limit configuration](/http-api/instance/#limit-configuration-object).
|
|
|
|
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_limit_config`, target type `limit_config`, target ID `0`, and metadata keys `rule_count`, `changed_rule_count`, and `trait_definition_count`, each taken from the stored document. `changed_rule_count` is the number of rule identifiers added, removed, or stored with different filters or limits, compared with the configuration the serving node held before the write.
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|