diff --git a/cmd/memos/main.go b/cmd/memos/main.go index 5bc68849b..b3f55f0d2 100644 --- a/cmd/memos/main.go +++ b/cmd/memos/main.go @@ -67,6 +67,11 @@ var ( slog.Error("failed to migrate", "error", err) return } + if err := storeInstance.LoadDeploymentConfiguration(ctx); err != nil { + cancel() + slog.Error("failed to load deployment configuration", "error", err) + return + } s, err := server.NewServer(ctx, instanceProfile, storeInstance) if err != nil { diff --git a/docs/configuration-provisioning.md b/docs/configuration-provisioning.md new file mode 100644 index 000000000..0638e6371 --- /dev/null +++ b/docs/configuration-provisioning.md @@ -0,0 +1,497 @@ +# Configuration Provisioning + +Status: Implemented + +## Summary + +Memos should follow Mastodon's deployment-configuration model: configuration supplied by the deployment is loaded directly into each server process and +remains authoritative for that process lifetime. It is not imported into the database and is not tracked as database-owned application state. + +The first supported file-backed configuration resources are: + +- OAuth2 identity providers. +- Instance settings for general policy, storage, memo behavior, notifications, and AI providers. + +Memos scans `/etc/secrets` after database migration and demo seeding, validates every matching file, builds one immutable configuration snapshot, and +publishes that snapshot before HTTP or background services start. Applying a changed file requires a process restart. + +Every resource file contains exactly one existing `memos.store` protobuf message encoded as protobuf JSON. No resource envelope, state file, ownership +table, or second persistent copy of a secret is introduced. The process necessarily holds decoded secrets in its private runtime snapshot. + +## Design model + +Mastodon reads external authentication and other deployment configuration from environment variables or a dotenv file during process initialization. It +does not copy that configuration into an administrator-editable database resource or maintain Terraform-style ownership state. + +Memos should use the same lifecycle while adapting the input format to its existing generated store messages: + +- Mounted JSON files replace a large collection of environment variables. +- File-backed resources exist in the effective runtime configuration. +- Stored resources continue to exist in the database but are shadowed when a file declares the same stable key. +- UI and API mutations cannot change an actively file-backed resource. +- Removing a file and restarting removes the runtime override; it does not delete or modify the stored resource. + +This is deployment configuration, not resource reconciliation. Terms such as adoption, import, unmanage, drift, prune, and Terraform state do not apply. + +## Goals + +- Accept secrets through mounted files without committing them to seed SQL or command-line arguments. +- Keep each file equal to one generated store protobuf message. +- Load and validate the complete file set before exposing any of it. +- Make deployment configuration authoritative for the lifetime of the process. +- Preserve database-backed UI configuration for keys not supplied by files. +- Prevent API writes from appearing to change an effective file-backed resource. +- Keep secret values out of logs, API responses, caches that expose values, and additional persistence. +- Preserve the administrator password sign-in path when password sign-in is disabled for regular users. + +## Non-goals + +- Persist file contents or file ownership metadata in the database. +- Add a `provisioning_resource` table or provisioning columns to existing tables. +- Reconcile database state to match a desired resource graph. +- Delete database resources when files disappear. +- Support multiple configuration sources with precedence rules in the first version. +- Write UI changes back into mounted files. +- Watch files or reload configuration without restarting in the first version. +- Support partial field ownership within an instance-setting group. + +## Terminology + +**Stored configuration** +: Configuration stored in the existing `idp` and `system_setting` database tables. + +**Deployment configuration** +: Configuration decoded from matching files during process startup. + +**Effective configuration** +: The configuration used by APIs, authentication, and background services. Deployment configuration shadows stored configuration with the same stable key. + +**Stable key** +: The identity-provider UID or instance-setting key used to merge deployment and stored configuration. + +## File discovery + +Memos scans direct children of `/etc/secrets`. The directory may contain unrelated platform secrets; only supported filename patterns are read. Memos does +not recurse into subdirectories and does not create, modify, or delete anything in the directory. + +| Filename pattern | Protobuf message | Stable key | +| --- | --- | --- | +| `memos-idp-