v0.165.0: Indítópult megosztása — guest launcher via capability URL (+ optional password, QR)
Mint a 160-bit capability URL (/s/<token>) serving a standalone read-only guest launcher: same tiles, opens apps in new tabs, no account, no admin session. Information only, zero control — every privilege stays behind each app's own auth. - /s/ pre-auth pass-through (after the claim gate) + session-CSRF exemption; guest password POST carries its own pre-auth HMAC CSRF. - Constant-time token match; empty stored token = disabled = byte-identical mux 404. - Optional per-share password: separate bcrypt hash + own attempt map; signed cookie = HMAC(token|passwordHash) keyed with web.session_secret, so rotate/change invalidates. - Guest labels ride the v0.164.0 ruling; never expose internal state vocabulary. - Token redacted in logs (/s/<redacted>); never in CHANGELOG/REPORT/CONTEXT. - Admin modal: copy-link, QR (go-qrcode), set/clear password, rotate, disable. - Tests: Groups A-G (14) + 3 red-proofs verified red.
This commit is contained in:
@@ -1,5 +1,45 @@
|
||||
## Changelog
|
||||
|
||||
### v0.165.0 — Indítópult megosztása: guest launcher via capability URL (2026-07-24)
|
||||
|
||||
No agent coupling; MinAgent unchanged. New dependency: `github.com/skip2/go-qrcode`
|
||||
(v0.0.0-20200617195104-da1b6568686e, MIT, pure Go, zero transitive deps) for the modal QR code.
|
||||
|
||||
The admin launcher gains an **"Indítópult megosztása"** button that mints a **capability URL**
|
||||
(`https://<host>/s/<token>`, 160-bit token) serving a standalone, read-only guest launcher — same
|
||||
tiles, opens apps in new tabs — with **no accounts and no admin session**. The link grants
|
||||
**information only, zero control**: app names + public URLs; every privilege stays behind each app's
|
||||
own auth and the controller admin password.
|
||||
|
||||
- **Capability-URL serving.** `/s/<token>` is added to the RequireAuth pre-auth allowlist (AFTER the
|
||||
claim-gate block, so the claim gate stays supreme) and exempted from session CSRF (guests carry
|
||||
their own pre-auth HMAC CSRF, like the claim POST). Token comparison is `subtle.ConstantTimeCompare`;
|
||||
an empty stored token matches nothing, so a wrong/disabled token is **byte-identical to the mux
|
||||
default 404** — nothing distinguishes it from an unknown route. Guest responses set `X-Robots-Tag:
|
||||
noindex, nofollow`, `Referrer-Policy: no-referrer`, `Cache-Control: no-store`.
|
||||
- **Optional per-share password.** A SEPARATE credential — its own bcrypt hash
|
||||
(`settings.LauncherSharePasswordHash`, never the admin hash), its own per-IP 5/1-min attempt map
|
||||
(never the admin login map). Passing it once mints a signed cookie = HMAC-SHA256 over
|
||||
`token|passwordHash` (keyed with the persisted, box-scoped `web.session_secret`), so **rotating the
|
||||
token OR changing the password invalidates every outstanding cookie** with zero bookkeeping.
|
||||
- **Modal (admin):** copy-link, a QR code (`/launcher/share/qr.png`, ~256px, admin-authed),
|
||||
"Jelszó beállítása/törlése", "Új link készítése" (rotation), "Megosztás kikapcsolása". POSTs under
|
||||
`/launcher/share/*` ride the normal admin session + session CSRF.
|
||||
- **Guest state labels ride the v0.164.0 ruling:** `StateStopped` ⇒ "A tulajdonos leállította";
|
||||
any other non-clickable state ⇒ "Átmenetileg nem elérhető"; guests never see internal state
|
||||
vocabulary (stopped/exited/degraded/unhealthy). Clickable ⇔ operational AND its public route is
|
||||
published (`isOperationalState && !routeUnpublished`), so a guest tap never dead-ends on a 404.
|
||||
- **Token is a secret:** never logged (the ServeHTTP debug line and the 404 WARN redact `/s/` paths
|
||||
to `/s/<redacted>`), never written to CHANGELOG/REPORT/CONTEXT, constant-time comparison only.
|
||||
- **Refactors:** `launcherApps()` extracted from `launcherHandler` (shared with the guest handler);
|
||||
the tile visual extracted into a `launch_tile` partial (single markup source for admin + guest);
|
||||
`isOperationalState` promoted to a package predicate (single source for the funcmap + guest rule).
|
||||
- New files: `internal/web/share.go` (pure core), `internal/web/share_handlers.go` (HTTP surface),
|
||||
`internal/web/share_test.go` (Groups A–G + 3 red-proofs verified red), templates
|
||||
`launcher_shared.html` + `launcher_share_password.html`.
|
||||
- Design rulings (CONTEXT): member accounts are superseded by this capability-URL model;
|
||||
per-member tile visibility is parked under the SSO arc.
|
||||
|
||||
### v0.164.0 — Deliberately stopped apps no longer alarm (banner + email) (2026-07-24)
|
||||
|
||||
No agent coupling; MinAgent unchanged. Operator finding on 9201: stopping an app via the UI
|
||||
|
||||
Reference in New Issue
Block a user