d0e7e2eb51
gates / gates (push) Successful in 2s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
90 lines
9.9 KiB
Markdown
90 lines
9.9 KiB
Markdown
# FIRST-ADMIN — how each app gets its first admin account (the audit behind `09` §3 decision 45)
|
||
|
||
**The rule (operator, 2026-09-28): an app is never published with a login a stranger knows.** The box publishes every
|
||
app on the household's domain (`*.domain` through the tunnel). Where the box can set the first admin password, it
|
||
generates one at install and shows it on the app page. Where it cannot, the app stays in the catalog, and the install
|
||
dialog and the app page say what the default login is and to change it at once.
|
||
|
||
**Mechanisms (controller ≥ 0.279.0; the gate ≥ 0.280.0):**
|
||
- `after_install:` in `.felhom.yml` — one command in the app's own container after a FRESH install, with generated
|
||
deploy values filled in; `success:` marker required. Never after a restore or a kept-data load. (`internal/stacks/after_install.go`)
|
||
- The page's default-login rule — `app_info.default_creds` is shown, with the sentence „Ez az alkalmazás egy ismert,
|
||
közös jelszóval indul: %s. Telepítés után azonnal változtasd meg." / "This app starts with a known, shared password:
|
||
%s. Change it right after the install.", while that login is in effect; hidden once `after_install` replaced it.
|
||
(`internal/web/known_login.go`)
|
||
- The setup gate (`09` §3 decision 46, controller ≥ 0.280.0) — `setup_gate: true` (+ optional `setup_done_probe:`): a
|
||
fresh install of an open-first-run app is closed to everyone but the household until the app's own status says an
|
||
admin exists, or the household presses „Kész, beállítottam". Spike and verdict:
|
||
`felhom.eu/documentation/audits/login-gate-2026-09-29/B/B-VERDICT.md` (14 of 34 class-4 apps have a probe — 2
|
||
measured, 12 upstream; 20 need the button). **It does not close OPEN SIGN-UP after the setup** (R-711).
|
||
- „Megváltoztattam" / "I changed it" (controller ≥ 0.280.0, R-710) — the household's word under a known default login.
|
||
|
||
**Classes:** 1 generated by us at install · 2 set by the household in our dialog · 3 a hard-coded default · 4 an open
|
||
first-run screen (the first visitor creates the admin) · 5 no login by design · 6 unknown.
|
||
**Sources:** **M** = measured on a box (named) · **R** = read in this catalog · **U** = upstream, read or remembered,
|
||
NOT measured. Every U must be measured before a fix is built on it.
|
||
|
||
**Status 2026-09-29:** 5 fixed by a generated first password (bookstack, calibre-web, claper, mealie, wger — every
|
||
class-3 app); 4 class-4 apps gated (immich, n8n, audiobookshelf, uptime-kuma). **30 class-4 apps remain open** — the
|
||
next sessions gate them app by app (R-707): a probe measured on 9202 where the app has one, the button where it has
|
||
none. Until an app is gated, its risk is the window until the household opens it first.
|
||
|
||
| app | class | how the first admin exists | fix route | status | source |
|
||
|---|---|---|---|---|---|
|
||
| actualbudget | 4 | first visitor sets the server password | (b) `POST /account/bootstrap` | open | R, harness fixture |
|
||
| adventurelog | 4 | open sign-up | (a) `DJANGO_ADMIN_*` env; (b) `createsuperuser --noinput` | open | R; U (env) |
|
||
| **audiobookshelf** | 4 | first visitor creates root | **setup gate**, probe `GET /status` → `isInit` | **GATED** — catalog, 2026-09-29 | **M 9202**: as immich (the probe, read from upstream, measured: opened ~20 s after `POST /init`); its app's login 200 after |
|
||
| bentopdf | 5 | browser-only PDF tool, no accounts | – | fine | R |
|
||
| **bookstack** | 3 | `admin@admin.com / password` | (b) `artisan bookstack:create-admin --initial` | **FIXED** — catalog, decision 45 | **M 9202**: default fails, generated works; **M demo-hp**: default still works on the installed app (unchanged, page warns) |
|
||
| calcom | 4 | first visitor becomes admin (`/api/auth/setup`) | (b) `POST /api/auth/setup`; (a) `NEXT_PUBLIC_DISABLE_SIGNUP` | open | **M 9202** (setup 200 once, then 400) |
|
||
| **calibre-web** | 3 | `admin / admin123` | (b) `cps.py -p /config/app.db -s admin:<pw>` as `abc`; `generate: password:24:special` (controller ≥ 0.280.0) | **FIXED** — catalog, 2026-09-29 | **M 9202**: fresh install — default refused, generated signs in (302), wrong refused; **M demo-hp**: the installed app's password changed by the operator's ruling (Part A) |
|
||
| **claper** | 3 (+ open sign-up) | seeds `admin@claper.co / claper` | (b) `bin/claper rpc … update_user_password` | **FIXED** — catalog, decision 45 | **M 9202**: default fails, generated works, a restore keeps it |
|
||
| code-server | 1 | `PASSWORD` generated, applied every start | – | fine | R |
|
||
| crafty-controller | 1 | `CRAFTY_PASSWORD` → default.json | – | fine | R |
|
||
| docmost | 4 | first registered user is admin | (b) `POST /api/auth/setup` | open | R, fixture |
|
||
| emby | 4 | setup wizard | (b) `/Startup/*` API | open | U |
|
||
| ghost | 4 | `/ghost/` setup | (b) `POST /ghost/api/admin/authentication/setup/` | open | U |
|
||
| gitea | 4 | web installer open (no `INSTALL_LOCK`) | (a) `INSTALL_LOCK` + (b) `gitea admin user create` | open | R; R-624 |
|
||
| glance | 5 | config-file dashboard, no users | – | fine | R |
|
||
| gokapi | 1 | `GOKAPI_PASSWORD` before first serve | – | fine | R |
|
||
| grafana | 1 | `GF_SECURITY_ADMIN_PASSWORD` — **falls back to `admin` if empty** (R-708) | – | fine while the field is set | R |
|
||
| gramps-web | 4 | first-run onboarding | (b) `python3 -m gramps_webapi user add` | open | U |
|
||
| home-assistant | 4 | onboarding | (b) `POST /api/onboarding/users` | open | R, fixture |
|
||
| homebox | 4 | open registration | (b) register API; (a) disable registration after | open | U |
|
||
| homepage | 5 | static start page | – | fine | R |
|
||
| **immich** | 4 | first visitor admin sign-up | **setup gate**, probe `GET /api/server/config` → `isInitialized` | **GATED** — catalog, 2026-09-29 (decision 46) | **M 9202**: stranger 302→gate page / 401 during and after the install; household in 0.2 s; probe opened it 0–20 s after the sign-up; phone-app API (Bearer) 200 after (`felhom.eu/documentation/audits/login-gate-2026-09-29/C/`) |
|
||
| jellyfin | 4 | startup wizard | (b) `/Startup/*` | open | U |
|
||
| kimai | 1 | `ADMIN_PASSWORD` → `ADMINPASS` | – | fine | R |
|
||
| komga | 4 | first visitor claims | (b) `POST /api/v1/claim` | open | U |
|
||
| **mealie** | 3 | `changeme@example.com / MyPassword` | (b) its own user repository (`update_password`), password as `sys.argv[1]` | **FIXED** — catalog, 2026-09-29 | **M 9202**: fresh install — default 401, generated 200, wrong 401 (`felhom.eu/documentation/audits/login-gate-2026-09-29/D/`) |
|
||
| **n8n** | 4 | owner setup | **setup gate**, probe `GET /rest/settings` → `data.userManagement.showSetupOnFirstLoad` = false | **GATED** — catalog, 2026-09-29 | **M 9202**: as immich; opened by the probe ~20 s after the owner setup |
|
||
| navidrome | 4 | first user is admin | (a) `ND_DEVAUTOCREATEADMINPASSWORD`; (b) `/auth/createAdmin` | open | R, fixture; U (env) |
|
||
| nextcloud | 1 | `NEXTCLOUD_ADMIN_PASSWORD` → auto-install | – | fine | R; **M** demo-hp 2026-09-28 |
|
||
| onlyoffice | 5 | JWT-protected API, no login screen | – | fine | R |
|
||
| opengist | 4 | first registered user is admin | (b) `POST /register`; (a) `OG_DISABLE_SIGNUP` | open | R, fixture |
|
||
| outline | 4 | e-mail / magic-link onboarding | (c) none found (R-624) | open — page note needed | R |
|
||
| paperless-ngx | 1 | `PAPERLESS_ADMIN_PASSWORD` | – | fine | R |
|
||
| papra | 4 | open e-mail sign-up | (b) sign-up API; (a) disable registration | open | R, fixture |
|
||
| plant-it | 4 | open, `USERS_LIMIT=-1` | (a) `USERS_LIMIT=1` + (b) sign-up | open | U |
|
||
| plex | 2 | the household's plex.tv claim token | – | fine | R |
|
||
| privatebin | 5 | anonymous pastes by design | – | fine | R |
|
||
| radarr | 4 | first visitor sets auth | (b) `PUT /api/v3/config/host` with the apikey | open | U |
|
||
| rallly | 4 | magic link to any e-mail | (a) `INITIAL_ADMIN_EMAIL` + `ALLOWED_EMAILS` | open | U |
|
||
| recipe-importer | 4 | our own image, open until set | (c) now; our own code | open | R |
|
||
| romm | 4 (catalog said 3) | first unauthenticated `POST /api/users` creates the user (harness); the stale `admin / admin` note is **removed** (2026-09-29) | (b) `POST /api/users`; or the setup gate | open | **M demo-hp** (401 for the old note); fixture |
|
||
| seerr | 4 | setup wizard (needs a media server) | (c) | open | U |
|
||
| sonarr | 4 | as radarr | (b) | open | U |
|
||
| sparkyfitness | 4 | open registration | (a) `SPARKY_FITNESS_ADMIN_EMAIL` + disable sign-up | open | U |
|
||
| tandoor | 4 | setup page while no users | (b) `createsuperuser --noinput` | open | R, fixture |
|
||
| termix | 4 | first registered user is admin | (b) user-create API | open | U |
|
||
| **uptime-kuma** | 4 | first visitor creates admin (socket.io `setup`) | **setup gate**, no HTTP probe → the household's „Kész, beállítottam" | **GATED** — catalog, 2026-09-29 | **M 9202**: gate held across a controller restart; the press opened it (second press 409). The proof pressed WITHOUT doing the socket.io setup — the press trusts the household |
|
||
| vaultwarden | 1 | `ADMIN_TOKEN` generated; invite-only (R-512) | – | fine | R |
|
||
| vikunja | 4 | open registration | (b) `vikunja user create`; (a) disable registration | open | R, fixture |
|
||
| wanderer | 4 | `PUBLIC_DISABLE_SIGNUP=false` | (a) disable after first user | open | U |
|
||
| **wger** | 3 | `admin / adminadmin` | (b) Django `set_password`, password as `sys.argv[1]` | **FIXED** — catalog, 2026-09-29; + R-712 (a browser's https Origin was refused by CSRF) | **M 9202**: fresh install — default refused, generated signs in (302), wrong refused, with the browser's https Origin |
|
||
| wishlist | 4 | first sign-up is admin | (b) `POST /signup` | open | R, fixture |
|
||
| zipline | 4 (catalog said 3) | v4 sets up on first run; the stale `admin / zipline` note is **removed** (2026-09-29) | (b) setup API (U); or the setup gate (probe `/api/setup` `firstSetup`, U) | open | R; audit logs |
|
||
|
||
**Counts (computed from the table, 2026-09-29):** class 1: 8 · class 2: 1 · class 3: 5 · class 4: 34 · class 5: 5.
|
||
**Fixed: 5** (every class-3 app). **Gated: 4.** **Open: 30** (class 4). Fine: 14.
|