feat: instance administration, bootstrap admin, and managed-tier OAuth gate

Self-hosted instances are now email/password only — OAuth providers
(GitHub/Google/Discord/Microsoft) activate solely when PRIMORA_MANAGED=true,
reserved for the future hosted tier.

Sign-up lifecycle: the first public sign-up becomes the instance admin; after
bootstrap, sign-up closes by default. A better-auth databaseHook gates only
/sign-up/email, so admin-created users and OAuth flows bypass it. The login
page reads GET /instance/public to hide the sign-up tab, show the bootstrap
notice, and render only the providers the instance offers.

Instance settings live in core.settings (migration 00010) and resolve
in-app > env > built-in default, cached 15s. Platform admins (better-auth
user.role) manage sign-up, mail transport, and rate limits via
GET/PUT/DELETE /instance/settings or the new Settings → Instance tab. Secret
values are AES-256-GCM encrypted at rest with PRIMORA_ENCRYPTION_KEY, never
returned by the API, and decrypted by the auth service at send time.

Auth service reads the same settings table for the sign-up gate and mail
config, so in-app changes take effect without a restart.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-21 17:46:47 +02:00
1 parent 1214108d6b
commit c6166de886
36 files changed
+1729 -109

No files matched your search

+24 -2
View File
@@ -61,10 +61,32 @@ The important ones:
| `POSTGRES_*` / `DATABASE_URL` | Postgres connection |
| `DRAGONFLY_URL` | Cache/rate-limit store |
| `SMTP_*` / `MAIL_FROM` | Transactional email (Mailpit in dev, or Resend via `RESEND_API_KEY`) |
| `GITHUB_CLIENT_ID` / `GOOGLE_CLIENT_ID` / `DISCORD_*` / `MICROSOFT_*` | Optional OAuth providers |
| `USER_RATE_LIMIT_PER_MINUTE` / `API_KEY_RATE_LIMIT_PER_MINUTE` | Throttling, `0` disables |
| `SIGNUP_ENABLED` | Open public sign-up after bootstrap — default `false`; admins override in-app |
| `PRIMORA_MANAGED` | Managed tier flag — enables OAuth providers when `true`; self-hosted is email/password only |
| `USER_RATE_LIMIT_PER_MINUTE` / `API_KEY_RATE_LIMIT_PER_MINUTE` | Throttling defaults — overridable in-app |
| `VITE_DEMO_MODE` | Enable the client-side demo workspace |
### Instance administration
The **first public sign-up becomes the instance admin** — on a fresh database the
login page only offers sign-up. After that, public sign-up is closed and the
admin can reopen it in-app.
Admins get a **Settings → Instance** tab that manages the instance without a
restart: public sign-up, mail transport (Resend or SMTP), and rate limits.
Every setting resolves in the order **in-app value → `.env` → built-in
default**; resetting a key reverts to env/default. Secrets (SMTP password,
Resend key) are stored AES-256-GCM-encrypted and never displayed back.
API equivalents:
```bash
GET /api/v1/instance/public # unauthenticated — login page state
GET /api/v1/instance/settings # platform admin
PUT /api/v1/instance/settings/{key} # platform admin — {"value": ...}
DELETE /api/v1/instance/settings/{key} # platform admin — revert to env/default
```
## Verification
```bash