diff --git a/CHANGELOG.md b/CHANGELOG.md index 9577b77..69fc127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## The setup gate on four apps; every hard-coded default replaced; stale notes; grafana; wger's sign-in (2026-09-29, `09` §3 decisions 45–46) + +- **The setup gate (decision 46, controller ≥ 0.280.0):** `setup_gate: true` on **immich**, **n8n**, **audiobookshelf** + (each with a `setup_done_probe:` measured to flip on 9202) and **uptime-kuma** (no HTTP status: the household's + „Kész, beállítottam"). A fresh install is closed to everyone but the household until the first setup is done. +- **mealie**, **wger**, **calibre-web**: a generated `ADMIN_PASSWORD` + `after_install:` through the app's own code + (mealie's user repository; Django `set_password`; `cps.py -s` as `abc`). calibre-web's field is + `generate: "password:24:special"` (its policy). mealie and wger pass the password as `sys.argv[1]`, never inside the + code (a security review of the drill commit caught the first shape). Proven on 9202 on fresh installs: the default is + refused, the generated one signs in, a wrong one is refused. +- **wger — R-712:** behind traefik a browser's https Origin was refused with "CSRF verification failed" (nobody could + sign in from a browser). `CSRF_TRUSTED_ORIGINS=https://${SUBDOMAIN}.${DOMAIN}` + `X_FORWARDED_PROTO_HEADER_SET=True`. +- **romm, zipline:** the stale `default_creds` notes (a login that does not exist) are gone; first steps say to create + the admin on the first visit. +- **grafana — R-708:** `GF_SECURITY_ADMIN_PASSWORD=${…:?…}` — compose refuses to start without it; never `admin`. +- `FIRST-ADMIN.md`: 5 fixed (every class-3 app), 4 gated, 30 open. README + REUSE: the gate convention and the + argv rule. Copy freeze recaptured (diff = those apps' strings only). + ## No app goes live with a login a stranger knows — first two apps; the audit (2026-09-28 evening, `09` §3 decision 45) - **`FIRST-ADMIN.md`** (new): how each of the 53 apps gets its first admin — class, fix route, status, and whether each diff --git a/FIRST-ADMIN.md b/FIRST-ADMIN.md index 81740ac..a53e0e5 100644 --- a/FIRST-ADMIN.md +++ b/FIRST-ADMIN.md @@ -5,32 +5,39 @@ app on the household's domain (`*.domain` through the tunnel). Where the box can 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):** +**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-28:** 2 apps fixed (claper, bookstack). 37 apps of class 3/4 remain (3 of class 3, 34 of class 4) — they are the work of the next -sessions (R-707). Until each is fixed, a class-3 app shows the sentence (its `default_creds` is in the catalog); a -class-4 app has no default to show, so its risk is the window until the household opens it first. +**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 | (b) `POST /init` | open | R, fixture | +| **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:` — **needs a password with a special character** (its policy), our generator has none | open — needs a controller generator | **M 9202** (default works; `-s` refused an alphanumeric one); **M demo-hp**: default works (page warns) | +| **calibre-web** | 3 | `admin / admin123` | (b) `cps.py -p /config/app.db -s admin:` 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 | @@ -45,12 +52,12 @@ class-4 app has no default to show, so its risk is the window until the househol | 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 | (b) `POST /api/auth/admin-sign-up` | open | U | +| **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) login + users API | open | R; fixture (login works) | -| n8n | 4 | owner setup | (b) `POST /rest/owner/setup` | open | R, fixture | +| **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 | @@ -64,19 +71,19 @@ class-4 app has no default to show, so its risk is the window until the househol | 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) | catalog note `admin / admin` is **stale**: on demo-hp it answers 401 like a wrong password; a first unauthenticated `POST /api/users` created the user (harness) | (b) `POST /api/users` | open — **the page warns with a login that does not exist** | **M demo-hp** (401); fixture | +| 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 | (b) socket.io `setup` | 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) `manage.py shell -c` set_password | open | R | +| **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) | catalog note `admin / zipline` looks **stale** (v4 sets up on first run) | (b) setup API (U) | open | R; audit logs | +| 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):** class 1: 8 · class 2: 1 · class 3: 5 (bookstack, calibre-web, claper, mealie, wger; -romm and zipline were listed as 3 and are 4) · class 4: 34 · class 5: 5. **Fixed: 2.** **Open: 37.** Fine: 14. +**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. diff --git a/README.md b/README.md index 0775aaf..be31c74 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,29 @@ Never run after a restore or a kept-data load. Keep `app_info.default_creds`: th succeeded, and says "This app starts with a known, shared password…" while it is still in effect. Per-app status: `FIRST-ADMIN.md`. +**Never paste `${ADMIN_PASSWORD}` into program code** (a `python3 -c` string, a Django `shell -c`, an Elixir `rpc` +string): a quote in a household-typed password breaks or changes the program. Pass it as its OWN argument +(`["python3", "-c", "...sys.argv[1]...", "${ADMIN_PASSWORD}"]`, mealie and wger) or inside a plain argument +(`--password=${ADMIN_PASSWORD}`, calibre-web's `admin:${ADMIN_PASSWORD}`). An app whose policy demands a special +character uses `generate: "password:24:special"` (controller ≥ 0.280.0: a lower, an upper, a digit and one of +`-_.!@#%+=`). + +### The setup gate (`setup_gate`, controller ≥ 0.280.0) + +An app whose FIRST VISITOR creates the admin is installed closed to everyone but the household (a browser signed in +to the dashboard) until its first setup is done (`09` §3 decision 46): + +```yaml +setup_gate: true +setup_done_probe: # optional — without it the household presses "Done, I set it up" + url: http://n8n:5678/rest/settings # the app's own read-only status, on the docker network + field: data.userManagement.showSetupOnFirstLoad # dotted JSON path + done: "false" # its value once an admin exists, as text +``` + +The url uses the app's `container_name` and its internal port. **Measure the probe on 9202 before and after the +setup** (it must flip), and prove a stranger gets the gate page / 401 until then. Per-app status: `FIRST-ADMIN.md`. + ### App-email mapping (`smtp_mapping`) Apps that can send outbound email (password resets, invites, confirmations) get it through diff --git a/REPORT.md b/REPORT.md index a7f8511..cc044f8 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,11 +1,19 @@ -# REPORT — 2026-09-28 evening: no app goes live with a login a stranger knows (decision 45) — the audit and the first two apps +# REPORT — 2026-09-29: the setup gate on four apps; every hard-coded default replaced (decisions 45–46) -Architecture: `09-update-architecture.md` §3 decision 45 (new); `01-topology-and-trust.md` links the audit. +Architecture: `09-update-architecture.md` §3 decisions 45–46; `01-topology-and-trust.md` §5. Controller 0.280.0. +Evidence: `felhom.eu/documentation/audits/login-gate-2026-09-29/` (B spike, C gate live, D defaults live). -| app | before | route | proof (9202, fresh install, controller 0.279.0) | live | -|---|---|---|---|---| -| claper | seeds admin@claper.co / claper (measured: authenticates) | `bin/claper rpc` update_user_password | default fails · generated works · wrong fails · restore keeps it | `9dc8a05` | -| bookstack | admin@admin.com / password (measured, also on demo-hp) | `artisan bookstack:create-admin --initial` | default fails · generated works · wrong fails | this commit | -| calibre-web | admin / admin123 (measured, also on demo-hp) | `cps.py -s` refuses an alphanumeric password | — | open: needs a special-character generator | +| app | change | proof on 9202 (drill catalog, fresh install, controller 0.280.0) | +|---|---|---| +| immich | setup gate, probe `isInitialized` | stranger: gate page / 401 during install and until setup; household in 0.2 s; probe opened it; phone-app API 200 after | +| n8n | setup gate, probe `showSetupOnFirstLoad` | as immich; opened ~20 s after the owner setup | +| audiobookshelf | setup gate, probe `/status isInit` | as immich; opened ~20 s after `POST /init` | +| uptime-kuma | setup gate, the household's button | held across a controller restart; the press opened it; a second press 409 | +| mealie | `after_install` (argv) | default 401, generated 200, wrong 401 | +| wger | `after_install` (argv) + R-712 CSRF | default refused, generated signs in with a browser's https Origin, wrong refused | +| calibre-web | `after_install`, `password:24:special` | default refused, generated signs in, wrong refused | +| romm, zipline | stale `default_creds` removed | the page no longer names a login that does not exist | +| grafana | R-708 `${…:?…}` | `docker compose config`: empty/unset refused (rc 1), set → rc 0 | -The full table: `FIRST-ADMIN.md`. Evidence: `felhom.eu/documentation/audits/logins-nvme-2026-09-28/B/`. +Gates: the catalog pre-push gates; `check-copy-i18n.py --capture-freeze` (diff = only these apps' strings). +Status: `FIRST-ADMIN.md` — 5 fixed, 4 gated, 30 open (R-707). diff --git a/REUSE.md b/REUSE.md index 2ffb63f..aa00173 100644 --- a/REUSE.md +++ b/REUSE.md @@ -21,7 +21,8 @@ Templates are config; the few script helpers other scripts must REUSE, never re- | **The one canonical example app** | `templates/paperless-ngx/` (both files) | Multi-container (app + postgres + redis), HDD + userdata mounts, full deploy_fields spectrum (domain/subdomain/secret/password/text/path/select). Copy this structure for any new app. | | `.felhom.yml` required fields | `templates/paperless-ngx/.felhom.yml` | All 53 apps: `display_name`, `description` (Hungarian), `category`, `subdomain`, `slug`, `resources{mem_request, mem_limit, pi_compatible, needs_hdd}`, `deploy_fields`, `app_info{tagline, use_cases, first_steps, ...}`, `healthcheck`. Optional: `smtp_mapping` (email-capable apps), `open_path` (non-root landing page, e.g. ghost). | | deploy_fields conventions | `templates/paperless-ngx/.felhom.yml` (`deploy_fields:` block) | Every app starts with `DOMAIN` (type `domain`) + `SUBDOMAIN` (type `subdomain`, `locked_after_deploy: true`). Secrets: `type: secret` + `generate:` — dominant generators `password:24` (DB passwords) and `hex:32` (app secret keys); `password:16` for shown admin passwords (`type: password`). HDD apps add `HDD_PATH` (`type: path`, placeholder `/mnt/felhom-drives/hdd_1`, locked). Labels/descriptions in Hungarian. | -| **Known default login → `after_install:`** (decision 45, controller ≥ 0.279.0) | `templates/bookstack/.felhom.yml` (`ADMIN_PASSWORD` field + `after_install:` block); `FIRST-ADMIN.md` for every app | An app that starts with a known admin login gets a generated `type: password` field (`generate: "password:24"`, `locked_after_deploy: true`) and ONE `after_install: {service, env: [ADMIN_PASSWORD], command: [...], success: ""}` through the app's OWN CLI, run once after a FRESH install. Keep `app_info.default_creds` — the page hides it once the command succeeded and warns while it is in effect. **Prove on 9202 (drill catalog) before live: the default fails, the generated password works, a wrong one fails.** TRAPS: `success:` is required because a CLI can exit 0 on an error (claper's `rpc`); our generator is letters+digits only, so an app with a special-character password policy (calibre-web) needs a new generator first; a Hungarian first-steps change needs `check-copy-i18n.py --capture-freeze`. | +| **Known default login → `after_install:`** (decision 45, controller ≥ 0.279.0) | `templates/bookstack/.felhom.yml` (`ADMIN_PASSWORD` field + `after_install:` block); `FIRST-ADMIN.md` for every app | An app that starts with a known admin login gets a generated `type: password` field (`generate: "password:24"`, `locked_after_deploy: true`) and ONE `after_install: {service, env: [ADMIN_PASSWORD], command: [...], success: ""}` through the app's OWN CLI, run once after a FRESH install. Keep `app_info.default_creds` — the page hides it once the command succeeded and warns while it is in effect. **Prove on 9202 (drill catalog) before live: the default fails, the generated password works, a wrong one fails.** TRAPS: `success:` is required because a CLI can exit 0 on an error (claper's `rpc`); **pass the password as its own argument, never inside program code** (`sys.argv[1]` — mealie, wger; security review 2026-09-29); a special-character policy uses `generate: "password:24:special"` (controller ≥ 0.280.0, calibre-web); a Hungarian first-steps change needs `check-copy-i18n.py --capture-freeze`. | +| **Open first-run screen → `setup_gate:`** (decision 46, controller ≥ 0.280.0) | `templates/n8n/.felhom.yml` (probe), `templates/uptime-kuma/.felhom.yml` (no probe → the household's button); `FIRST-ADMIN.md` | `setup_gate: true` + optional `setup_done_probe: {url: http://:/, field: , done: ""}` | TRAPS: the probe must FLIP on the setup — measure it before and after on 9202; an app with open sign-up after setup (R-711) is not closed by the gate; `url` is read on the docker network, so it names the container, not the subdomain. | | Controller-side health probe | `templates/vaultwarden/.felhom.yml` (`healthcheck:` block) | `healthcheck.checks[]` with `type: http` (port only), `type: api` (port + `path` + `expect.status: 200`), or `type: tcp` (port only — mealie, crafty-controller). Prefer `api` with a real health path when the app has one. | | App lifecycle (`available`/`hidden`/`abandoned`) | `templates/plant-it/.felhom.yml` (`lifecycle:` block) | Optional top-level `lifecycle:` in `.felhom.yml`. Absent/empty ≡ `available`. `hidden` = not offered for new installs; `abandoned` = same, PLUS a permanent "Nem karbantartott" badge + notice on every box already running it. **Deployed instances keep full function in both states** — lifecycle governs what is OFFERED, never what runs; the controller refuses a deploy of a non-available template server-side (fail-closed, so a stale link or direct POST cannot install one). Unknown value → treated as `available` + one WARN, never a broken template. **Do NOT take an app out of circulation by deleting or moving its directory** — that orphans every customer already running it, which is what the 2026-07-21 `retired/` experiment got wrong. The resolvability gate skips non-available apps, so an abandoned app's dead image is not a standing red. | | **Catalog gates — THE entry point** | `scripts/catalog_gates.py` | **Run `python3 scripts/catalog_gates.py ` after ANY template change** (mandated in `CLAUDE.md`). Runs all four gates below in order — image-pins, image-resolvable, volume-persistence, engine-major (2026-09-13; git-history diff, hook-only until CI fetches deeper, R-452) — and exits **non-zero if any fails**; **2 (UNDETERMINED) is reported distinctly and is never a pass**, 1 (convicted) outranks 2 in the summary. Naming app(s) scopes the two gates that accept scoping, which is the normal after-a-change run; with no names the RUNTIME gate deploys every template, so that form is **scratch host only**. **Why a runner** (operator ruling 2026-08-02, R-161): the only gates in this project that ever get run are the ones with a single entry point named in a CLAUDE.md — `felhom.eu/scripts/site_gates.py` is run, R-29's three orphans are named nowhere and have stopped nothing. Controller-side enforcement was rejected because a load-time check reads only the file and a static audit reports the catalog clean **including papra** — it would pass on the very defect it exists to catch; CI was rejected for now (neither repo has any, no users yet). Adding a fourth gate here means adding it to `GATES` in this file — nothing else. | diff --git a/scripts/copy_freeze/hu.json b/scripts/copy_freeze/hu.json index 3b4495f..79f181a 100644 --- a/scripts/copy_freeze/hu.json +++ b/scripts/copy_freeze/hu.json @@ -126,9 +126,8 @@ "calibre-web": { "app_info.default_creds": "admin / admin123", "app_info.first_steps[0]": "Nyisd meg a books.DOMAIN címet a böngészőben", - "app_info.first_steps[1]": "Jelentkezz be: admin / admin123", - "app_info.first_steps[2]": "Változtasd meg azonnal a jelszót", - "app_info.first_steps[3]": "Dobj egy könyvet a Fájlkezelőben (FileBrowser) az import/calibre mappába — automatikusan feldolgozza és a media/books könyvtárba helyezi", + "app_info.first_steps[1]": "Jelentkezz be: admin és a Beállítások oldalon látható első jelszó", + "app_info.first_steps[2]": "Dobj egy könyvet a Fájlkezelőben (FileBrowser) az import/calibre mappába — automatikusan feldolgozza és a media/books könyvtárba helyezi", "app_info.prerequisites[0]": "Külső HDD szükséges az e-könyvek tárolásához", "app_info.prerequisites[1]": "x86 processzor szükséges (a CWA tartalmazza a Calibre binárist)", "app_info.prerequisites[2]": "Legalább 768 MB szabad RAM ajánlott", @@ -140,6 +139,8 @@ "app_info.use_cases[4]": "KOReader szinkronizáció (olvasási pozíció, könyvjelzők)", "data_paths[calibre].label": "Beolvasandó e-könyvek", "data_paths[media/books].label": "E-könyvtár", + "deploy_fields[ADMIN_PASSWORD].description": "Az első bejelentkezéshez: admin és ez a jelszó. Kell benne kis- és nagybetű, szám és egy különleges karakter.", + "deploy_fields[ADMIN_PASSWORD].label": "Admin jelszó (admin)", "deploy_fields[DOMAIN].description": "A szerver domain neve", "deploy_fields[DOMAIN].label": "Domain", "deploy_fields[HDD_PATH].description": "A külső merevlemez elérési útja, ahol a Calibre könyvtár található", @@ -530,8 +531,8 @@ "mealie": { "app_info.default_creds": "changeme@example.com / MyPassword", "app_info.first_steps[0]": "Nyisd meg a mealie.DOMAIN címet a böngészőben", - "app_info.first_steps[1]": "Jelentkezz be: changeme@example.com / MyPassword", - "app_info.first_steps[2]": "Változtasd meg azonnal az email címet és jelszót", + "app_info.first_steps[1]": "Jelentkezz be: changeme@example.com és a Beállítások oldalon látható első jelszó", + "app_info.first_steps[2]": "Változtasd meg az email címet a sajátodra", "app_info.first_steps[3]": "Importáld az első receptet egy weboldal URL beillesztésével", "app_info.first_steps[4]": "Próbáld ki az étkezés tervezőt", "app_info.tagline": "Receptkezelő és étkezés tervező a családnak", @@ -540,6 +541,8 @@ "app_info.use_cases[2]": "Heti étkezés tervezés és bevásárlólista generálás", "app_info.use_cases[3]": "Receptek megosztása családtagokkal és barátokkal", "app_info.use_cases[4]": "Többnyelvű felület - magyar is elérhető", + "deploy_fields[ADMIN_PASSWORD].description": "Az első bejelentkezéshez: changeme@example.com és ez a jelszó. Utána a profilodban módosítható.", + "deploy_fields[ADMIN_PASSWORD].label": "Admin jelszó (changeme@example.com)", "deploy_fields[DOMAIN].description": "A szerver domain neve", "deploy_fields[DOMAIN].label": "Domain", "deploy_fields[SUBDOMAIN].description": "Az alkalmazás aldomainje", @@ -861,9 +864,8 @@ "description": "Magyar receptoldalak importálása Mealie-be és Tandoor-ba" }, "romm": { - "app_info.default_creds": "admin / admin", "app_info.first_steps[0]": "Nyisd meg az arcade.DOMAIN címet a böngészőben", - "app_info.first_steps[1]": "Jelentkezz be az alapértelmezett admin / admin fiókkal", + "app_info.first_steps[1]": "Az első megnyitáskor hozd létre az admin fiókodat", "app_info.first_steps[2]": "Változtasd meg azonnal a jelszót a Settings menüben", "app_info.first_steps[3]": "Töltsd fel a ROM fájlokat a Fájlkezelőben (FileBrowser) a roms/ mappába, platform mappákba rendezve (pl. roms/gba/, roms/snes/)", "app_info.first_steps[4]": "Indíts egy Scan-t a bal oldali menüben a ROM-ok beolvasásához", @@ -1089,8 +1091,8 @@ "wger": { "app_info.default_creds": "admin / adminadmin", "app_info.first_steps[0]": "Nyisd meg a fitness.DOMAIN címet a böngészőben", - "app_info.first_steps[1]": "Jelentkezz be: admin / adminadmin", - "app_info.first_steps[2]": "Változtasd meg azonnal a jelszót", + "app_info.first_steps[1]": "Jelentkezz be: admin és a Beállítások oldalon látható első jelszó", + "app_info.first_steps[2]": "Add meg az email címedet a beállításokban", "app_info.first_steps[3]": "Hozd létre az edzéstervedet", "app_info.first_steps[4]": "Kezdd el naplózni az edzéseidet", "app_info.tagline": "Edzésnapló - edzéstervek, haladás követés és testsúly napló", @@ -1099,6 +1101,8 @@ "app_info.use_cases[2]": "Gyakorlatok adatbázisa képekkel és leírásokkal", "app_info.use_cases[3]": "Kalória és tápanyag követés", "app_info.use_cases[4]": "API támogatás fitnesz alkalmazás integrációkhoz", + "deploy_fields[ADMIN_PASSWORD].description": "Az első bejelentkezéshez: admin és ez a jelszó. Utána a beállításokban módosítható.", + "deploy_fields[ADMIN_PASSWORD].label": "Admin jelszó (admin)", "deploy_fields[DOMAIN].description": "A szerver domain neve", "deploy_fields[DOMAIN].label": "Domain", "deploy_fields[SECRET_KEY].label": "Titkosítási kulcs", @@ -1124,12 +1128,10 @@ "description": "Családi kívánságlista megosztás" }, "zipline": { - "app_info.default_creds": "admin / zipline", "app_info.first_steps[0]": "Nyisd meg az img.DOMAIN címet a böngészőben", - "app_info.first_steps[1]": "Jelentkezz be: admin / zipline", - "app_info.first_steps[2]": "Változtasd meg azonnal a jelszót", - "app_info.first_steps[3]": "Konfiguráld a ShareX-et vagy Flameshot-ot az API URL-lel", - "app_info.first_steps[4]": "Tölts fel egy screenshot-ot a teszteléshez", + "app_info.first_steps[1]": "Az első megnyitáskor hozd létre az admin fiókodat", + "app_info.first_steps[2]": "Konfiguráld a ShareX-et vagy Flameshot-ot az API URL-lel", + "app_info.first_steps[3]": "Tölts fel egy screenshot-ot a teszteléshez", "app_info.prerequisites[0]": "ShareX (Windows) vagy Flameshot (Linux) ajánlott a screenshot feltöltéshez", "app_info.tagline": "Screenshot és fájlmegosztó szerver ShareX/Flameshot integrációval", "app_info.use_cases[0]": "Screenshotok automatikus feltöltése ShareX/Flameshot-ból", diff --git a/templates/audiobookshelf/.felhom.yml b/templates/audiobookshelf/.felhom.yml index 81cc898..7641f05 100644 --- a/templates/audiobookshelf/.felhom.yml +++ b/templates/audiobookshelf/.felhom.yml @@ -53,6 +53,17 @@ deploy_fields: description: "A külső merevlemez elérési útja" locked_after_deploy: true + +# --- The setup gate (controller >= 0.280.0, `09` §3 decision 46) --- +# The first visitor would create the admin. So a fresh install is closed to everyone but the household (a browser +# signed in to the dashboard) until the first setup is done; audiobookshelf's own status says so (`/status` isInit — upstream, measured on 9202 by the +# 2026-09-29 live proof). +setup_gate: true +setup_done_probe: + url: http://audiobookshelf:80/status + field: isInit + done: "true" + # --- App info (info page content) --- app_info: tagline: "Hangoskönyv és podcast kezelő, lejátszó és szinkronizáló" diff --git a/templates/calibre-web/.felhom.yml b/templates/calibre-web/.felhom.yml index baf3537..e0ef2ea 100644 --- a/templates/calibre-web/.felhom.yml +++ b/templates/calibre-web/.felhom.yml @@ -63,6 +63,16 @@ deploy_fields: description: "A külső merevlemez elérési útja, ahol a Calibre könyvtár található" locked_after_deploy: true + # `09` §3 decision 45: Calibre-Web starts with admin / admin123. The box replaces that password with this + # generated one right after the install (after_install below). Its password policy demands a special character, + # hence `:special` (controller >= 0.280.0). + - env_var: ADMIN_PASSWORD + label: "Admin jelszó (admin)" + type: password + generate: "password:24:special" + description: "Az első bejelentkezéshez: admin és ez a jelszó. Kell benne kis- és nagybetű, szám és egy különleges karakter." + locked_after_deploy: true + # --- App info (info page content) --- app_info: tagline: 'Automatizált e-könyv könyvtár - könyvfeldolgozás, formátumkonverzió és OPDS feed' @@ -78,8 +88,7 @@ app_info: first_steps: - 'Nyisd meg a books.DOMAIN címet a böngészőben' - - 'Jelentkezz be: admin / admin123' - - 'Változtasd meg azonnal a jelszót' + - 'Jelentkezz be: admin és a Beállítások oldalon látható első jelszó' - 'Dobj egy könyvet a Fájlkezelőben (FileBrowser) az import/calibre mappába — automatikusan feldolgozza és a media/books könyvtárba helyezi' prerequisites: @@ -87,6 +96,16 @@ app_info: - 'x86 processzor szükséges (a CWA tartalmazza a Calibre binárist)' - 'Legalább 768 MB szabad RAM ajánlott' +# --- After a fresh install (controller >= 0.280.0, decision 45) --- +# Calibre-Web's OWN `cps.py -s user:password`, as the app user. Measured on 9202 2026-09-29: afterwards admin123 no +# longer signs in, the new one does; a password without a special character is refused by its policy. +after_install: + service: calibre-web + user: abc + env: [ADMIN_PASSWORD] + command: ["python3", "/app/calibre-web-automated/cps.py", "-p", "/config/app.db", "-s", "admin:${ADMIN_PASSWORD}"] + success: "Password for user 'admin' changed" + # --- Controller-side health probe --- healthcheck: checks: @@ -110,8 +129,7 @@ i18n: - 'KOReader sync (reading position, bookmarks)' first_steps: - 'Open books.DOMAIN in your browser' - - 'Sign in: admin / admin123' - - 'Change the password straight away' + - 'Sign in: admin and the first password shown on the settings page' - 'Drop a book into the import/calibre folder in the File manager (FileBrowser) - it is processed and filed under media/books on its own' prerequisites: - 'An external hard drive is needed to keep the e-books on' @@ -128,6 +146,9 @@ i18n: label: 'E-book library path' description: 'The path to the external hard drive where the Calibre library lives' placeholder: '/mnt/felhom-drives/hdd_1' + - env_var: ADMIN_PASSWORD + label: 'Admin password (admin)' + description: 'For the first sign-in: admin and this password. It needs a lower and an upper case letter, a digit and a special character.' data_paths: - path: 'calibre' label: 'E-books to read in' diff --git a/templates/grafana/docker-compose.yml b/templates/grafana/docker-compose.yml index 4e0b8d5..da43dd6 100644 --- a/templates/grafana/docker-compose.yml +++ b/templates/grafana/docker-compose.yml @@ -15,7 +15,7 @@ services: environment: - TZ=Europe/Budapest - GF_SERVER_ROOT_URL=https://${SUBDOMAIN}.${DOMAIN} - - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD:-admin} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD:?the admin password is required (R-708)} volumes: - grafana_data:/var/lib/grafana networks: diff --git a/templates/immich/.felhom.yml b/templates/immich/.felhom.yml index 5087ea7..abce739 100644 --- a/templates/immich/.felhom.yml +++ b/templates/immich/.felhom.yml @@ -63,6 +63,17 @@ deploy_fields: description: "A külső merevlemez elérési útja, ahol a fotók és videók tárolódnak" locked_after_deploy: true + +# --- The setup gate (controller >= 0.280.0, `09` §3 decision 46) --- +# The first visitor would create the admin. So a fresh install is closed to everyone but the household (a browser +# signed in to the dashboard) until the first setup is done; immich's own status says so (measured on 9202 2026-09-29: +# isInitialized false -> true once the admin exists). +setup_gate: true +setup_done_probe: + url: http://immich-server:2283/api/server/config + field: isInitialized + done: "true" + # --- App info (info page content) --- app_info: tagline: 'Google Photos alternatíva - automatikus fotó mentés és rendszerezés' diff --git a/templates/mealie/.felhom.yml b/templates/mealie/.felhom.yml index e7ee33c..8e3043e 100644 --- a/templates/mealie/.felhom.yml +++ b/templates/mealie/.felhom.yml @@ -40,6 +40,15 @@ deploy_fields: locked_after_deploy: true description: "Az alkalmazás aldomainje" + # `09` §3 decision 45: Mealie starts with changeme@example.com / MyPassword. The box replaces that password with + # this generated one right after the install (after_install below); the app page shows it as the first password. + - env_var: ADMIN_PASSWORD + label: "Admin jelszó (changeme@example.com)" + type: password + generate: "password:24" + description: "Az első bejelentkezéshez: changeme@example.com és ez a jelszó. Utána a profilodban módosítható." + locked_after_deploy: true + # --- App info (info page content) --- app_info: tagline: 'Receptkezelő és étkezés tervező a családnak' @@ -55,11 +64,22 @@ app_info: first_steps: - 'Nyisd meg a mealie.DOMAIN címet a böngészőben' - - 'Jelentkezz be: changeme@example.com / MyPassword' - - 'Változtasd meg azonnal az email címet és jelszót' + - 'Jelentkezz be: changeme@example.com és a Beállítások oldalon látható első jelszó' + - 'Változtasd meg az email címet a sajátodra' - 'Importáld az első receptet egy weboldal URL beillesztésével' - 'Próbáld ki az étkezés tervezőt' +# --- After a fresh install (controller >= 0.279.0, decision 45) --- +# Mealie's OWN user repository sets the seeded user's password (what its scripts/change_password.py does, without the +# prompts). Measured on 9202 2026-09-29: afterwards MyPassword answers 401 at /api/auth/token, the new one 200. +after_install: + service: mealie + env: [ADMIN_PASSWORD] + # The password is the LAST ARGUMENT (sys.argv[1]), never pasted into the code: a quote in it cannot break or change + # the program (security review 2026-09-29). + command: ["python3", "-c", "import sys\nfrom mealie.core.security.security import hash_password\nfrom mealie.db.db_setup import session_context\nfrom mealie.repos.repository_factory import AllRepositories\nwith session_context() as s:\n r = AllRepositories(s, group_id=None, household_id=None)\n u = r.users.get_one('changeme@example.com', 'email')\n r.users.update_password(u.id, hash_password(sys.argv[1]))\n print('FELHOM_AFTER_INSTALL_OK')\n", "${ADMIN_PASSWORD}"] + success: "FELHOM_AFTER_INSTALL_OK" + # --- Controller-side health probe --- healthcheck: checks: @@ -98,8 +118,8 @@ i18n: - 'A multilingual interface - English and Hungarian among them' first_steps: - 'Open mealie.DOMAIN in your browser' - - 'Sign in: changeme@example.com / MyPassword' - - 'Change the e-mail address and password straight away' + - 'Sign in: changeme@example.com and the first password shown on the settings page' + - 'Change the e-mail address to your own' - 'Import your first recipe by pasting a web page address' - 'Try the meal planner' deploy_fields: @@ -109,6 +129,9 @@ i18n: - env_var: SUBDOMAIN label: 'Subdomain' description: 'The subdomain this app answers on' + - env_var: ADMIN_PASSWORD + label: 'Admin password (changeme@example.com)' + description: 'For the first sign-in: changeme@example.com and this password. Change it in your profile afterwards.' # update_ladder — the test record: one tested step per line, oldest first (JSON flow mappings, # `09-update-architecture.md` §6.4 part 4). WRITTEN BY scripts/upgrade-test.py, never by hand; diff --git a/templates/n8n/.felhom.yml b/templates/n8n/.felhom.yml index cd15659..c333ced 100644 --- a/templates/n8n/.felhom.yml +++ b/templates/n8n/.felhom.yml @@ -41,6 +41,17 @@ deploy_fields: generate: "hex:16" locked_after_deploy: true + +# --- The setup gate (controller >= 0.280.0, `09` §3 decision 46) --- +# The first visitor would create the admin. So a fresh install is closed to everyone but the household (a browser +# signed in to the dashboard) until the first setup is done; n8n's own settings say so (measured on 9202 2026-09-29: +# showSetupOnFirstLoad true -> false once the owner exists). +setup_gate: true +setup_done_probe: + url: http://n8n:5678/rest/settings + field: data.userManagement.showSetupOnFirstLoad + done: "false" + # --- App info (info page content) --- app_info: tagline: "Vizuális workflow automatizálás - Zapier/IFTTT alternatíva" diff --git a/templates/romm/.felhom.yml b/templates/romm/.felhom.yml index fec621f..765317c 100644 --- a/templates/romm/.felhom.yml +++ b/templates/romm/.felhom.yml @@ -80,7 +80,6 @@ deploy_fields: # --- App info (info page content) --- app_info: tagline: "Retró játékgyűjtemény kezelő, böngésző és lejátszó" - default_creds: "admin / admin" docs_url: "https://github.com/rommapp/romm/wiki" use_cases: @@ -92,7 +91,7 @@ app_info: first_steps: - "Nyisd meg az arcade.DOMAIN címet a böngészőben" - - "Jelentkezz be az alapértelmezett admin / admin fiókkal" + - "Az első megnyitáskor hozd létre az admin fiókodat" - "Változtasd meg azonnal a jelszót a Settings menüben" - "Töltsd fel a ROM fájlokat a Fájlkezelőben (FileBrowser) a roms/ mappába, platform mappákba rendezve (pl. roms/gba/, roms/snes/)" - "Indíts egy Scan-t a bal oldali menüben a ROM-ok beolvasásához" @@ -184,7 +183,6 @@ i18n: app_info: tagline: "Retro game collection manager, browser and player" - default_creds: "admin / admin" use_cases: - "Sort and browse a retro game collection in your browser" @@ -195,7 +193,7 @@ i18n: first_steps: - "Open arcade.DOMAIN in your browser" - - "Sign in with the default admin / admin account" + - "On the first visit, create your admin account" - "Change the password straight away, in the Settings menu" - "Upload the ROM files into the roms/ folder in the File manager (FileBrowser), sorted into platform folders (for example roms/gba/, roms/snes/)" - "Start a Scan from the menu on the left to read the ROMs in" diff --git a/templates/uptime-kuma/.felhom.yml b/templates/uptime-kuma/.felhom.yml index 316ce8a..96b6ae3 100644 --- a/templates/uptime-kuma/.felhom.yml +++ b/templates/uptime-kuma/.felhom.yml @@ -35,6 +35,13 @@ deploy_fields: locked_after_deploy: true description: "Az alkalmazás aldomainje" + +# --- The setup gate (controller >= 0.280.0, `09` §3 decision 46) --- +# The first visitor would create the admin. So a fresh install is closed to everyone but the household (a browser +# signed in to the dashboard) until the first setup is done. uptime-kuma says it only over socket.io, so the household presses "Done, I set it up" +# on the app page. +setup_gate: true + # --- App info (info page content) --- app_info: tagline: "Szolgáltatás monitoring - értesítés ha valami nem működik" diff --git a/templates/wger/.felhom.yml b/templates/wger/.felhom.yml index 2016da3..a06a7ba 100644 --- a/templates/wger/.felhom.yml +++ b/templates/wger/.felhom.yml @@ -41,6 +41,15 @@ deploy_fields: generate: "hex:32" locked_after_deploy: true + # `09` §3 decision 45: wger starts with admin / adminadmin. The box replaces that password with this generated + # one right after the install (after_install below); the app page shows it as the first password. + - env_var: ADMIN_PASSWORD + label: "Admin jelszó (admin)" + type: password + generate: "password:24" + description: "Az első bejelentkezéshez: admin és ez a jelszó. Utána a beállításokban módosítható." + locked_after_deploy: true + # --- App info (info page content) --- app_info: tagline: "Edzésnapló - edzéstervek, haladás követés és testsúly napló" @@ -56,12 +65,23 @@ app_info: first_steps: - 'Nyisd meg a fitness.DOMAIN címet a böngészőben' - - 'Jelentkezz be: admin / adminadmin' - - 'Változtasd meg azonnal a jelszót' + - 'Jelentkezz be: admin és a Beállítások oldalon látható első jelszó' + - 'Add meg az email címedet a beállításokban' - 'Hozd létre az edzéstervedet' - 'Kezdd el naplózni az edzéseidet' +# --- After a fresh install (controller >= 0.279.0, decision 45) --- +# Django's own set_password on the seeded admin. Measured on 9202 2026-09-29: afterwards adminadmin no longer signs in +# at /en/user/login, the new one does. +after_install: + service: wger + env: [ADMIN_PASSWORD] + # The password is the LAST ARGUMENT (sys.argv[1]), never pasted into the code: a quote in it cannot break or change + # the program (security review 2026-09-29). Django is set up the way manage.py does it (settings.main). + command: ["python3", "-c", "import os, sys; sys.path.insert(0, '/home/wger/src'); os.chdir('/home/wger/src'); os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.main'); import django; django.setup(); from django.contrib.auth.models import User; u = User.objects.get(username='admin'); u.set_password(sys.argv[1]); u.save(); print('FELHOM_AFTER_INSTALL_OK')", "${ADMIN_PASSWORD}"] + success: "FELHOM_AFTER_INSTALL_OK" + # --- Controller-side health probe --- healthcheck: checks: @@ -87,8 +107,8 @@ i18n: - 'An API, for fitness app integrations' first_steps: - 'Open fitness.DOMAIN in your browser' - - 'Sign in: admin / adminadmin' - - 'Change the password straight away' + - 'Sign in: admin and the first password shown on the settings page' + - 'Add your e-mail address in the settings' - 'Build your training plan' - 'Start logging your workouts' deploy_fields: @@ -100,3 +120,6 @@ i18n: description: 'The subdomain this app answers on' - env_var: SECRET_KEY label: 'Encryption key' + - env_var: ADMIN_PASSWORD + label: 'Admin password (admin)' + description: 'For the first sign-in: admin and this password. Change it in the settings afterwards.' diff --git a/templates/wger/docker-compose.yml b/templates/wger/docker-compose.yml index 8fd5466..f02c78f 100644 --- a/templates/wger/docker-compose.yml +++ b/templates/wger/docker-compose.yml @@ -22,6 +22,10 @@ services: # A DATABASE a wger_data kötetre mutat (/home/wger/db), oda, ahol a wger # saját alapértelmezett sqlite fájlja is volt -- így meglévő telepítés # adatai nem "tűnnek el" egy másik útvonalra. + # R-712 (measured 2026-09-29 on 9202): behind traefik wger saw the request as http and refused a browser's + # https Origin with "CSRF verification failed" — nobody could sign in from a browser. + - CSRF_TRUSTED_ORIGINS=https://${SUBDOMAIN}.${DOMAIN} + - X_FORWARDED_PROTO_HEADER_SET=True - DJANGO_DB_ENGINE=django.db.backends.sqlite3 - DJANGO_DB_DATABASE=/home/wger/db/database.sqlite - DJANGO_DB_USER=wger diff --git a/templates/zipline/.felhom.yml b/templates/zipline/.felhom.yml index cb8c1a4..68c8361 100644 --- a/templates/zipline/.felhom.yml +++ b/templates/zipline/.felhom.yml @@ -50,7 +50,6 @@ deploy_fields: # --- App info (info page content) --- app_info: tagline: "Screenshot és fájlmegosztó szerver ShareX/Flameshot integrációval" - default_creds: "admin / zipline" docs_url: "https://zipline.diced.sh/docs/" use_cases: @@ -62,8 +61,7 @@ app_info: first_steps: - 'Nyisd meg az img.DOMAIN címet a böngészőben' - - 'Jelentkezz be: admin / zipline' - - 'Változtasd meg azonnal a jelszót' + - 'Az első megnyitáskor hozd létre az admin fiókodat' - 'Konfiguráld a ShareX-et vagy Flameshot-ot az API URL-lel' - 'Tölts fel egy screenshot-ot a teszteléshez' @@ -90,7 +88,6 @@ i18n: description: 'A ShareX/Flameshot server - screenshots and file sharing' app_info: tagline: 'A screenshot and file sharing server that works with ShareX/Flameshot' - default_creds: 'admin / zipline' use_cases: - 'Screenshots upload themselves from ShareX/Flameshot' - 'Shorten a URL and share text (paste)' @@ -99,8 +96,7 @@ i18n: - 'User accounts and invitations' first_steps: - 'Open img.DOMAIN in your browser' - - 'Sign in: admin / zipline' - - 'Change the password straight away' + - 'On the first visit, create your admin account' - 'Point ShareX or Flameshot at the API address' - 'Upload a screenshot to check it works' prerequisites: