Dynamic parameters¶
This document explains how the configuration of the platform works, the two mechanisms in play, their precedence, and the pitfalls to be aware of.
Overview: two separate systems¶
Gitrust uses two independent configuration systems:
| Système | Source | Modifiable à chaud | Visible dans l'UI admin |
|---|---|---|---|
| Configuration statique | Fichier .env + variables d'environnement |
Non (restart requis) | Non |
| Configuration dynamique | Table app_settings (PostgreSQL) |
Oui (effet immédiat) | Oui (/admin/settings) |
There is a hybrid case for OAuth (priority DB, .env in fallback).
1. Static configuration (.env)¶
Principle¶
Environment variables are loaded once at startup by dotenvy::dotenv() and stored in immutable Rust structs. They are never read again after boot.
Reference files¶
| Fichier | Rôle |
|---|---|
.env.example |
Template documenté avec toutes les variables |
.env |
Configuration locale (gitignore) |
.env.production |
Configuration de déploiement |
.env.test |
Configuration de tests |
Loading structures¶
| Struct | Variables concernées |
|---|---|
GitrustConfig |
GIT_REPOS_BASE_PATH, SSH_PORT, SSH_LISTEN_ADDR, SSH_HOST_KEY_PATH, CI_*, IMPORT_* |
AppConfig |
APP_NAME, APP_THEME, APP_DEBUG |
AuthConfig |
JWT_SECRET, JWT_EXPIRATION_MINUTES, SESSION_*, RATE_LIMIT_*, COOKIE_* |
EmailConfig |
SMTP_*, EMAIL_*, IMAP_* |
Behavior¶
- Loaded into
RustwardenApp::build()at startup - Stored in
Arc<Config>shared via axum state - Changing the
.envwithout restarting has no effect - These variables are NOT displayed in the admin interface
List of static variables (non-exhaustive)¶
DATABASE_URL, SERVER_HOST, SERVER_PORT, RUST_LOG,
SSH_HOST_KEY_PATH, SSH_PORT, SSH_LISTEN_ADDR, SSH_PUBLIC_HOST,
JWT_SECRET, JWT_EXPIRATION_MINUTES, JWT_ISSUER,
REFRESH_TOKEN_EXPIRATION_DAYS, REMEMBER_ME_EXPIRATION_DAYS,
SESSION_TIMEOUT_MINUTES, SESSION_BACKEND,
RATE_LIMIT_LOGIN_PER_MINUTE, RATE_LIMIT_REFRESH_PER_MINUTE,
RATE_LIMIT_GENERAL_PER_MINUTE,
APP_DEBUG, COOKIE_SECURE, COOKIE_SAME_SITE,
ADMIN_USERNAME, ADMIN_EMAIL, ADMIN_PASSWORD,
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM,
EMAIL_BASE_URL, EMAIL_QUEUE_*,
APP_NAME, APP_THEME, DEFAULT_LOCALE,
GIT_REPOS_BASE_PATH,
CI_ENABLED, CI_MAX_CONCURRENT, CI_REMOTE_HOST, CI_WORKSPACE_PATH
2. Dynamic configuration (app_settings table)¶
Principle¶
Dynamic settings are stored in the PostgreSQL database in the app_settings table and read on each request via AppSettingsService. They can be modified on-the-fly by an administrator from the /admin/settings interface.
Table diagram¶
CREATE TABLE app_settings (
id UUID PRIMARY KEY,
key VARCHAR(100) UNIQUE NOT NULL,
value TEXT NOT NULL,
description TEXT,
updated_by UUID REFERENCES users(id) ON DELETE SET NULL,
updated_at TIMESTAMPTZ NOT NULL
);
Initialization at startup¶
At boot, AppSettingsService::initialize_default_settings() creates the default settings only if they do not yet exist in base:
let existing = Self::get_setting(db, key).await?;
if existing.is_none() {
// ... insert valeur par défaut
}
Critical consequence: once a setting exists in base (created on first startup or modified via the UI), it is never overwritten by a subsequent restart.
List of dynamic settings and their default values¶
| Clé | Défaut | Description |
|---|---|---|
app_domain |
env::var("APP_DOMAIN") ou "localhost" |
Domaine de l'application |
allow_registration |
false |
Autoriser l'inscription publique |
validation_email_required |
true |
Exiger la validation email |
audit_log_level |
INFO |
Niveau de log d'audit |
audit_log_actions |
["create","update","delete","reset_password"] |
Actions auditées |
password_min_length |
8 |
Longueur minimum mot de passe |
password_require_uppercase |
false |
Exiger des majuscules |
password_require_lowercase |
false |
Exiger des minuscules |
password_require_digits |
false |
Exiger des chiffres |
password_require_special |
false |
Exiger des caractères spéciaux |
password_change_require_email |
true |
Confirmation email pour changement mdp |
password_expiration_enabled |
false |
Activer l'expiration des mots de passe |
password_expiration_days |
0 |
Durée d'expiration (jours) |
password_expiration_alert_enabled |
false |
Alerte email avant expiration |
password_expiration_alert_days_before |
7 |
Jours avant expiration pour alerter |
oauth_enabled |
false |
Activer OAuth/SSO |
oauth_google_enabled |
false |
Activer Google OAuth |
oauth_github_enabled |
false |
Activer GitHub OAuth |
oauth_discord_enabled |
false |
Activer Discord OAuth |
oauth_microsoft_enabled |
false |
Activer Microsoft OAuth |
oauth_redirect_base_url |
"" |
URL de base pour les callbacks OAuth |
oauth_auto_register |
true |
Créer un compte auto au premier login OAuth |
oauth_link_existing_account |
true |
Lier un compte OAuth à un utilisateur existant |
oauth_{provider}_client_id |
"" |
Client ID du provider OAuth |
oauth_{provider}_client_secret |
"" |
Client secret (chiffré AES-256-GCM en base) |
oauth_microsoft_tenant |
"" |
Tenant Azure AD |
3. Hybrid case: OAuth¶
OAuth is the only subsystem that combines both sources. The logic in OAuthConfig::load():
Pour chaque réglage OAuth :
1. Chercher dans app_settings (DB)
→ si trouvé et non vide : utiliser cette valeur
2. Sinon : fallback sur la variable d'environnement
→ si absente : utiliser la valeur par défaut codée en dur
Important: OAuthConfig::load() is called at startup. The result is stored in an Arc and is not replayed dynamically. SO :
- Changing an OAuth setting via the admin UI requires a restart for
OAuthConfigto be reloaded - The
.envis only a fallback if the DB has no value
4. Precedence and restart behavior¶
Static settings (.env only)¶
| Action | Effet immédiat | Après restart |
|---|---|---|
Modifier .env |
Non | Oui |
Dynamic settings (DB only)¶
| Action | Effet immédiat | Après restart |
|---|---|---|
| Modifier via UI admin | Oui | Oui (valeur en DB persiste) |
Modifier le .env |
Aucun effet | Aucun effet |
Hybrid Settings (OAuth)¶
| Action | Effet immédiat | Après restart |
|---|---|---|
| Modifier via UI admin | Non (OAuthConfig est en Arc) | Oui (DB prime sur .env) |
Modifier .env |
Non | Seulement si aucune valeur en DB |
5. Main pitfall: ghost .env variables¶
Certain variables present in .env.example give the illusion of configuring settings which are in reality managed by the database:
Variable .env |
Réglage DB correspondant | La variable .env est-elle lue ? |
|---|---|---|
ALLOW_REGISTRATION=true |
allow_registration |
Non — valeur par défaut codée en dur ("false") |
EMAIL_VALIDATION_REQUIRED=true |
validation_email_required |
Non — valeur par défaut codée en dur ("true") |
OAUTH_ENABLED=true |
oauth_enabled |
Oui, mais seulement en fallback si la DB n'a pas de valeur |
For ALLOW_REGISTRATION and EMAIL_VALIDATION_REQUIRED:
- The
.envvariable is documented in.env.examplebut is never accessed byinitialize_default_settings() - The initial value is hardcoded in the service
- Only the value in the database (modifiable via
/admin/settings) is authentic
For app_domain, this is the only dynamic setting that reads .env as the initial seed.
6. Decision diagram¶
┌─────────────────────────────┐
│ Quel type de réglage ? │
└──────────┬──────────────────-┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
Infrastructure Fonctionnel OAuth
(port, JWT, SMTP) (inscription, (providers,
mots de passe) secrets)
│ │ │
▼ ▼ ▼
.env seul DB seule DB + fallback .env
│ │ │
▼ ▼ ▼
Restart requis Effet immédiat Restart requis
via /admin/ (OAuthConfig en Arc)
settings
7. Guide for administrators¶
Change an infrastructure setting¶
Edit .env (or .env.production) then restart the service:
Change a functional setting¶
Log in as administrator, go to /admin/settings, modify the value, and click "Save". The effect is immediate, no restart necessary.
Change an OAuth setting¶
Via /admin/settings, modify the OAuth values then restart the service (the OAuth config is loaded into memory at boot and is not reread dynamically).
Check the actual value of a setting¶
For dynamic settings, query the database directly:
For static settings, check the logs at startup (RUST_LOG=debug).
8. Guide for developers¶
Add a new dynamic setting¶
-
Add the
(key, default_value, description)tuple inAppSettingsService::initialize_default_settings(): -
Read the value in the code via the service:
-
The value will automatically appear in
/admin/settings.
Add a new static setting¶
- Add variable in
.env.examplewith full documentation - Read the variable in the corresponding
Configstruct viaenv::var() - The value will not be visible in the admin interface
Add hybrid setting (DB + fallback .env)¶
Follow the OAuth pattern in crates/rustwarden-core/src/config/oauth.rs: