Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
420e0fe
docs: add DB-backed module settings design spec
antosubash Apr 21, 2026
ec213a8
docs: add implementation plan for db-backed module settings
antosubash Apr 21, 2026
1bdda2b
feat(settings): add ModuleSettingsRegistry for per-module BaseSetting…
antosubash Apr 21, 2026
c775abf
feat(settings): add SettingsStore for module-namespaced overrides
antosubash Apr 21, 2026
5263a2d
feat(settings): add hydrate_settings and value_type inference
antosubash Apr 21, 2026
b27ae81
feat(settings): add register_module_settings helper + ModuleSettingsR…
antosubash Apr 21, 2026
395e397
feat(settings): add SettingsReloaded event
antosubash Apr 21, 2026
bc961d5
feat(settings): add apply_changes_and_reload with validation + Settin…
antosubash Apr 21, 2026
6fd5f54
feat(hosting): split Settings into BootstrapSettings (env) + HostSett…
antosubash Apr 21, 2026
22cf3df
fix(settings): rename test_events.py to avoid collision with framewor…
antosubash Apr 21, 2026
afc7181
feat(hosting): register HostSettings as package='host' in module regi…
antosubash Apr 21, 2026
e124654
feat(hosting): hydrate module settings from DB at start of lifespan
antosubash Apr 21, 2026
1e12950
refactor(settings): move SettingsSettings off env (DB-backed) and sel…
antosubash Apr 21, 2026
99a9659
refactor(users): move UsersSettings off env; gate signup route at req…
antosubash Apr 21, 2026
30906e2
refactor(background_tasks): move settings off env; mark Celery fields…
antosubash Apr 21, 2026
dee0088
refactor(file_storage): move settings off env; group fields by backend
antosubash Apr 21, 2026
d8c0338
refactor(datasets): move settings off env (DB-backed)
antosubash Apr 21, 2026
d074132
style: ruff format + fix lint issues after Phase 3 migration
antosubash Apr 21, 2026
20f8642
feat(settings): expose type, requires_restart, group on module settin…
antosubash Apr 21, 2026
b9b55ec
feat(settings): add GET/PUT/DELETE endpoints for per-module settings
antosubash Apr 21, 2026
a94ac3b
chore(settings): point /settings/modules view at ModulesEdit page (pe…
antosubash Apr 21, 2026
35d66e7
feat(settings-ui): add FieldInput component with type-driven rendering
antosubash Apr 21, 2026
5a64339
feat(settings-ui): add ModuleForm with typed inputs, grouping, reset,…
antosubash Apr 21, 2026
565c46f
feat(settings-ui): add ModulesEdit page (sidebar + main panel layout)
antosubash Apr 21, 2026
108cdfa
fix(settings-ui): satisfy biome (label htmlFor, no assign-in-expr, im…
antosubash Apr 21, 2026
c6cea16
chore(settings): remove read-only Modules page (replaced by ModulesEdit)
antosubash Apr 21, 2026
557b557
test(e2e): verify settings-UI toggle persists via admin REST API
antosubash Apr 21, 2026
a52f250
feat(settings): add sm-settings import-from-env CLI
antosubash Apr 21, 2026
7cb6c40
chore: shrink .env.example to bootstrap-only (DB, environment, secret…
antosubash Apr 21, 2026
c9de1ab
docs: rewrite README config section — bootstrap vars only + UI pointer
antosubash Apr 21, 2026
2b389bf
docs: add release note for DB-backed module settings
antosubash Apr 21, 2026
ac13194
refactor(settings): simplify — centralize helpers, dedupe, skip no-op…
antosubash Apr 21, 2026
66d5c02
fix(users): fall back to env vars for bootstrap_* seed fields
antosubash Apr 21, 2026
b487c3f
fix(e2e): wait for dashboard after login in settings UI test
antosubash Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 8 additions & 52 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,58 +1,14 @@
# Database
# Only SM_DATABASE_URL is required. Everything else has sensible defaults.
# All module-level settings (SMTP, Celery, users, etc.) are now managed in
# the admin UI at /settings/modules — no env vars needed.

# Database (required)
SM_DATABASE_URL=sqlite+aiosqlite:///./app.db
# SM_DATABASE_URL=postgresql+asyncpg://sm:sm@localhost:5432/simple_module

# App
# Process identity (production must override SM_SECRET_KEY)
SM_ENVIRONMENT=development
SM_SECRET_KEY=change-me-in-production
SM_VITE_DEV_URL=http://localhost:5050

# Users module — local email+password auth (replaces Keycloak)
#
# Signup mode: false = admin-invite only (default), true = public signup
SM_USERS_ALLOW_SIGNUP=false

# Token secrets — MUST change in production. Dev placeholders are visible in
# log lines so they're obvious to rotate.
SM_USERS_RESET_PASSWORD_TOKEN_SECRET=dev-reset-token-secret-change-me
SM_USERS_VERIFICATION_TOKEN_SECRET=dev-verify-token-secret-change-me

# Mailer: console (logs tokenized links at INFO) or smtp
SM_USERS_MAILER=console
SM_USERS_BASE_URL=http://localhost:8000
# SM_USERS_SMTP_HOST=
# SM_USERS_SMTP_PORT=587
# SM_USERS_SMTP_USERNAME=
# SM_USERS_SMTP_PASSWORD=
# SM_USERS_SMTP_FROM=no-reply@localhost
# SM_USERS_SMTP_TLS=true

# First-admin bootstrap (auto-create an admin on first boot iff users table
# is empty AND both are set). Leave blank to disable — use `sm-users
# create-admin` instead.
# SM_USERS_BOOTSTRAP_EMAIL=admin@example.com
# SM_USERS_BOOTSTRAP_PASSWORD=changeme

# Optional second seeded user with the "user" role. Seeded alongside the
# admin on first boot (same empty-table guard). When both env vars are set
# AND SM_ENVIRONMENT=development, the login page renders quick-login buttons
# for both accounts to speed up manual testing. Leave blank in production.
# SM_USERS_BOOTSTRAP_USER_EMAIL=user@example.com
# SM_USERS_BOOTSTRAP_USER_PASSWORD=changeme

# Cookie: set cookie_secure=false in dev (HTTPS-only otherwise)
SM_USERS_COOKIE_SECURE=false

# Multi-tenancy (default off).
# SM_MULTI_TENANT=false
# SM_TENANT_HEADER=

# BackgroundTasks module — Celery + Redis task queue.
# In docker-compose these point at the `redis` service host (redis://redis:6379/*).
SM_BG_TASKS_BROKER_URL=redis://localhost:6379/0
SM_BG_TASKS_RESULT_BACKEND=redis://localhost:6379/1
# How long a task can stay in `running` without a heartbeat before it's
# flipped to `stuck` by the beat sweep.
SM_BG_TASKS_STUCK_AFTER_SECONDS=300
# Purge terminal rows older than this (days) — keeps history bounded.
SM_BG_TASKS_RETENTION_DAYS=14
# Dev-only: Vite asset URL (ignored in production builds)
SM_VITE_DEV_URL=http://localhost:5050
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ Thumbs.db
.playwright-cli/*
.playwright-mcp/*
host/client_app/.playwright-cli/*
.superpowers/
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,26 @@ docs/

## Configuration

All settings are `SM_`-prefixed env vars. Defaults in `.env.example` cover local dev. Key knobs:
Local deployments only need one env var — everything else has sensible defaults and is managed in the admin UI at `/settings/modules`.

| Variable | Default | Notes |
| Variable | Default | Required |
|---|---|---|
| `SM_DATABASE_URL` | `sqlite+aiosqlite:///./app.db` | Async URL. Postgres: `postgresql+asyncpg://...` |
| `SM_ENVIRONMENT` | `development` | Anything else triggers strict module discovery |
| `SM_SECRET_KEY` | _(placeholder)_ | **Must** change in production — signs session cookies |
| `SM_USERS_ALLOW_SIGNUP` | `false` | Enable public signup (else admin-invite only) |
| `SM_USERS_MAILER` | `console` | `console` logs links; `smtp` uses SMTP config (see `.env.example`) |
| `SM_USERS_RESET_PASSWORD_TOKEN_SECRET` | _(dev placeholder)_ | **Must** change in production |
| `SM_USERS_VERIFICATION_TOKEN_SECRET` | _(dev placeholder)_ | **Must** change in production |
| `SM_USERS_BOOTSTRAP_EMAIL` | `` | First-admin email; combined with `SM_USERS_BOOTSTRAP_PASSWORD`, creates admin on first boot iff users table is empty |
| `SM_USERS_BOOTSTRAP_PASSWORD` | `` | Paired with above |
| `SM_MULTI_TENANT` | `false` | Set `true` to enable `TenantMiddleware` |
| `SM_TENANT_HEADER` | `` | Empty = token-only; set e.g. `X-Tenant-ID` to enable header fallback |
| `SM_DATABASE_URL` | `sqlite+aiosqlite:///./app.db` | Yes — async URL. Postgres: `postgresql+asyncpg://...` |
| `SM_ENVIRONMENT` | `development` | No — any value other than `development`, `test`, `testing` triggers strict discovery and placeholder-secret checks |
| `SM_SECRET_KEY` | `change-me-in-production` | No in dev; **must** be overridden in production |
| `SM_VITE_DEV_URL` | `http://localhost:5050` | Dev only |

Power users can still override the following bootstrap knobs via env if needed: `SM_DB_POOL_SIZE`, `SM_DB_MAX_OVERFLOW`, `SM_DB_POOL_PRE_PING`, `SM_DB_POOL_RECYCLE`, `SM_DEBUG`, `SM_LOG_LEVEL`, `SM_LOG_FORMAT`, `SM_MODULES_ENABLED`. These are needed before the DB connection is open.

All module-level settings — users, SMTP, Celery broker, file storage backend, etc. — live in the admin UI. After upgrading an existing deployment, run once:

```bash
uv run sm-settings import-from-env
```

to seed DB overrides from the current `SM_*` environment.

> **docker-compose note:** `docker-compose.yml` sets a few `SM_BG_TASKS_*` vars so Celery can reach the `redis` service by container hostname before the DB-backed settings are loaded. That's deployment plumbing — not a module config knob.

See `framework-conventions.md` for the settings-per-module convention.

Expand Down
15 changes: 15 additions & 0 deletions docs/release-notes/2026-04-21-db-backed-settings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# DB-backed module settings

Every `SM_<MODULE>_*` env var has moved to the admin UI at `/settings/modules`.
`.env` now only needs `SM_DATABASE_URL` in typical deployments.

## Upgrading

After deploying this release:

1. Run `uv run sm-settings import-from-env` once to seed the DB with your current environment values.
2. Remove the `SM_<MODULE>_*` entries from your `.env` / deployment config (they're no longer read).

## Breaking changes

Setting `SM_USERS_ALLOW_SIGNUP=true` (or any other `SM_<MODULE>_*`) in the environment no longer has any effect. Use the admin UI or the `sm-settings` CLI.
Loading