4ce2df0a2b
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 18s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 15s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 54s
69 lines
3.7 KiB
Markdown
69 lines
3.7 KiB
Markdown
# 04 — Settings & Feature Flags (Part 3)
|
|
|
|
Two layers of configuration — **per-user preferences** and **org-wide system settings /
|
|
feature flags** — with a clear precedence. Critically: **AI is governed by a feature flag
|
|
(admin, global), not merely a user preference.**
|
|
|
|
## User settings (per user)
|
|
Stored in `user_settings`; editable by the user.
|
|
| Setting | Values |
|
|
|---------|--------|
|
|
| `theme` | system · light · dark (dark-first default) |
|
|
| `inbox_layout` | density, pane layout, default view/lane, per-account or unified |
|
|
| `notifications` | channels, quiet hours, priority-only |
|
|
| `ai_prefs` | per-feature opt-in (summaries, replies, semantic, ask-inbox…) — **only effective if the flag allows** |
|
|
| `provider_prefs` | default account, sync frequency, signature per account |
|
|
|
|
## System settings (admin, org-wide)
|
|
Stored in `system_settings` (singleton); Admin-only ([05](05-admin-system.md)).
|
|
- `maintenance_mode` (off · read-only · locked-except-admin) · `default_theme` ·
|
|
`registration_open` · org display name · retention defaults.
|
|
|
|
## Feature flag system
|
|
`feature_flags` rows: `key · enabled · scope · rollout · description · updated_by/at`.
|
|
- **`scope = SystemOnly`** — a hard org-wide switch; users cannot override (e.g., `provider.microsoft`, `maintenance.readonly`).
|
|
- **`scope = UserOverridable`** — a default that a user preference can turn *off* (never *on* beyond what the flag permits) — e.g., `ai.summaries`.
|
|
- **`rollout`** (jsonb) — optional per-role/percentage gating (e.g., enable a beta for Admins first).
|
|
|
|
### Evaluation service
|
|
```csharp
|
|
public interface IFeatureFlags {
|
|
bool IsEnabled(string key, UserContext user); // system flag ∧ scope ∧ role rollout
|
|
}
|
|
public interface IAiGate { // the AI-specific resolver
|
|
bool IsAiEnabled(UserContext user); // ai.enabled (system) ∧ user.ai_prefs.master
|
|
bool IsAiFeatureEnabled(string feature, UserContext user); // ∧ ai.<feature> ∧ user opt-in
|
|
}
|
|
```
|
|
- Flags are **cached** with change notification (hot-reload on admin edit); every read is cheap.
|
|
- All flag reads are **fail-closed**: unknown/errored flag ⇒ treated as **off**.
|
|
|
|
## AI gating precedence (the key requirement)
|
|
Effective AI availability is an **AND** down a chain — the system flag is the master gate:
|
|
```
|
|
AI feature X is available for user U ⇔
|
|
feature_flags["ai.enabled"].enabled (admin master switch — SYSTEM)
|
|
∧ feature_flags["ai." + X].enabled (per-feature flag — SYSTEM)
|
|
∧ providerCapability(X) (Ollama/provider actually available)
|
|
∧ user.ai_prefs.master_opt_in (user hasn't disabled AI for themselves)
|
|
∧ user.ai_prefs[X] (user opted into this feature)
|
|
```
|
|
- **Admin turns `ai.enabled` off ⇒ AI vanishes for everyone**, regardless of any user
|
|
preference. This is the behaviour the brief mandates.
|
|
- With AI on at the system level, users still choose per-feature. The **Null AI provider +
|
|
capability flags** (blueprint [06](../06-ai-strategy.md)) mean a disabled path **falls back
|
|
or hides** — never errors, never blocks core email.
|
|
|
|
## Settings precedence (general)
|
|
```
|
|
system default → feature flag (may hard-disable) → user override (only where UserOverridable)
|
|
```
|
|
|
|
## Maintenance mode
|
|
- `read-only`: mutations (send/cleanup/label) blocked with a banner; browsing/search stay up.
|
|
- `locked-except-admin`: only Admins can use the app (for migrations/upgrades).
|
|
- Enforced at the API via a middleware policy + surfaced as a global banner in the UI.
|
|
|
|
## Auditing
|
|
Flag and system-setting changes are **admin actions → audit-logged** (who/what/old→new/when).
|