Commit Graph

39 Commits (ee111bfa9ac84baee7402d89763cbc7aabcce2a7)

Author SHA1 Message Date
boban 06b5600641 docs(plan): release-promotion phase A implementation plan
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:33:03 +02:00
boban 30034a564b docs(spec): full release model — channels, dashboard buttons, two phases
Capture the maintainer's release workflow: dev-dashboard buttons drive the
pipeline (Deploy to Staging / Deploy to Public / Promote to Stable). Beta =
vX.Y.Z-betaN on staging, Stable = vX.Y.Z on public; public serves both channels.
Public promotion triggered via the GitHub API with a deploy token in the
encrypted vault. Decomposed: Phase A = git/CI/CD + config-URL infra (buildable
now, URL-agnostic); Phase B = the dashboard release-control UI (own spec, after
A + the repos exist).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:34:21 +02:00
boban 70da2718fb docs(spec): clarify URL handling — public default in config, private URLs only in .env
Per the maintainer: dev/staging may hold any URL (in their gitignored .env);
the public repo's tracked files carry only the public URL. So config/clusev.php
defaults to the public URL, private (Gitea/GitHub-private) URLs live solely in
gitignored .env, and install.sh derives CLUSEV_REPOSITORY from the clone origin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 11:22:34 +02:00
boban e2b4a968c9 docs(spec): 3-repo release promotion (Gitea → GitHub private → public)
Design for promoting Clusev dev → staging → public across three repos with
GitHub Actions CI, a manual gate before public, and a clean public history.
Core: no repository URL in tracked files — derived from the clone origin, kept
in gitignored .env per environment, so the public repo never exposes the
private/Gitea URLs and the URLs plug in later when the GitHub repos exist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 07:03:24 +02:00
boban 6f9091d3fa docs(wg): SP2 Phase 5 plan — first-time setup from the dashboard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 08:04:21 +02:00
boban 95a5811544 docs(wg): SP2 Phase 4 plan — settings + gate from the UI
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 00:48:11 +02:00
boban f91d8ed834 docs(wg): SP2 Phase 3 plan — peer management + write-bridge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 00:24:44 +02:00
boban eff836a1fb docs(wg): SP2 Phase 2 plan — traffic history + SVG chart
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:56:51 +02:00
boban dfdb0ce732 docs(wg): SP2 Phase 1 plan — live-status dashboard page
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:26:15 +02:00
boban 19fe1b8915 docs(wg): SP2 design — full WireGuard management in the dashboard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:21:45 +02:00
boban fa7e1c428e docs(wg): manual runbook for the WireGuard gate (SP1)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:50:54 +02:00
boban f154ee8aaf docs(wg): SP1 implementation plan — WireGuard gate + clusev wg CLI
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:20:51 +02:00
boban f0210c302b docs: spec — WireGuard panel gate + clusev wg CLI (SP1)
Optional host-level WireGuard gate for the Clusev panel: install.sh installs
wireguard-tools (off), a 'clusev wg setup/up/down/status/add-peer/remove-peer'
host CLI sets up wg0 + gates TCP 80/443 to the WG subnet via a dedicated
CLUSEV-WG-GATE iptables chain off DOCKER-USER, with 'clusev wg down' as the
escape hatch and SSH/WG-UDP never gated. Hardened after an adversarial review:
exact ordered firewall rules, persistence skips if wg0 is down (no reboot
brick), down isolates Clusev's chain, setup idempotency, precise install
wiring (CLUSEV_DIR exec + install_host_watchers). SP1 of 2; dashboard
status/traffic is SP2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 19:48:47 +02:00
boban 9756f9d463 docs: implementation plan — control-plane brute-force ban (Anmeldeschutz)
9-task TDD plan: BannedIp model, BruteforceGuard (inet_pton CIDR + cache),
ValidIpOrCidr rule, guests-only BlockBannedIp middleware + 403 page, Login/2FA
failure hooks + audit, clusev:unban CLI, Anmeldeschutz settings tab with R5
confirm-modal unban. Spec erratum: middleware is appended (not prepended) so
Auth::guest() resolves for the guests-only check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 17:20:52 +02:00
boban fe8c25c824 docs: spec — control-plane brute-force IP ban (Anmeldeschutz)
App-level persistent IP ban for Clusev's own login (distinct from the remote-fleet
Fail2banService): BannedIp model, BruteforceGuard service, guests-only BlockBannedIp
middleware, Login/2FA hooks + audit, a new Anmeldeschutz settings tab, clusev:unban CLI.
Hardened after an adversarial multi-lens review: documented IP trust model (Caddy sole
proxy, prod-only trustProxies), guests-only enforcement so an authed operator is never
locked out and can self-unban, current-IP self-whitelist, concrete cache strategy,
inet_pton CIDR matching, R5 confirm modals, errors.php for the 403 strings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 17:05:52 +02:00
boban 4ea4050c80 fix(auth): R15 review — restore one-time-code autofill, memoise pendingUser in verify
- backup field: autocomplete=off -> one-time-code (backup codes ARE one-time codes;
  matches the main challenge field, the rest of the app, and the spec; restores
  password-manager OTP autofill that the earlier polish broke)
- both verify() use the trait's memoised $this->pendingUser() instead of a fresh
  User::find(), saving a redundant query on the failed-attempt re-render (Pint dropped
  the now-unused User import in TwoFactorBackup)
- spec: align trait backing-state visibility note to the implemented 'private'

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 16:19:40 +02:00
boban 8ec3b0b69a docs: implementation plan — 2FA backup-code own button + view
Task-by-task TDD plan for Spec 1: shared CompletesTwoFactorChallenge trait,
new TwoFactorBackup component + guest route + view, main-view redesign
(form gated to TOTP users, backup button, mount->backup redirect for
key-only-no-secure-context), i18n keys, and full R12/R15 verification.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 15:12:04 +02:00
boban 0f5d14a252 docs: spec — split 2FA backup code into its own button + view
Spec 1 of 2 for the 2FA challenge rework. Moves the backup (recovery) code
off the combined challenge screen into a dedicated /two-factor-challenge/backup
route + view, behind a 'Backup-Code verwenden' button; the main screen shows
only the primary factor. A key-only user without secure context (http + bare IP)
is redirected straight to the backup view. Shared rate-limit/login logic is
extracted into a CompletesTwoFactorChallenge trait so both views hit one bucket
set. IP-vs-domain factor routing is already correct (WebauthnService::available)
and unchanged. Spec 2 (auth-failure -> fail2ban hard IP ban) is out of scope here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 15:01:39 +02:00
boban 503f4d7fdf docs: implementation plan for clusev host CLI + short commands + help tab-URL 2026-06-19 22:45:05 +02:00
boban 83bd42f16a docs: spec for clusev host CLI, short commands, help tab-URL 2026-06-19 22:37:37 +02:00
boban ca693219bf docs(plan): implementation plan for the in-panel help page
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 22:06:08 +02:00
boban 60295a0aea docs(spec): in-panel Help page design + drop product name from external-proxy hint
Spec for a bilingual /help page (left topic nav like Settings) covering all
settings, a generic reverse-proxy setup guide, and the 2FA access-path note
(security key only over the HTTPS domain; backup code on the bare-IP/HTTP path;
TOTP works everywhere). Also generalize the external-TLS hint copy (no product
name).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 22:02:25 +02:00
boban 4f1ea8ef0f docs(spec): dashboard "request certificate" button (trigger Caddy on-demand TLS)
DNS pre-check + internal SNI handshake to caddy to trigger on-demand issuance, with a
persisted status (no page-load ACME side effects), per-user throttle, external-TLS-mode
gating, and the control-plane-never-locked constraint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 18:24:25 +02:00
boban db179ad98a docs(spec): de-Claude repo + git-history rewrite runbook (beta-prep #2)
Deferred runbook for the final beta-prep step: purge CLAUDE.md, rules.md,
handoff.md, docs/session-handoff.md and docs/superpowers from all history,
strip Co-Authored-By trailers, keep the files locally + gitignored, then a
gated force-push. Execute at beta-cut time only, behind explicit confirmation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:30:23 +02:00
boban c54932b532 docs(spec): installer overhaul (Docker install, clusev user, DNS check) + themed MOTD
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:07:52 +02:00
boban cb3360b235 docs(spec): SMTP-aware forgot-password (15-min link + 2FA fallback)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 18:47:53 +02:00
boban 6290cec6ab fix(2fa): bound the backup-code reveal flag with a 10-minute TTL
The 2fa.codes_fresh reveal flag is now a timestamp (set at enrollment) and
RecoveryCodes::mount() only reveals within [now-600s, now], symmetric to the
download-grant window. A reveal flag whose modal never opened can no longer
re-reveal the codes later in the session. FirstFactorCodesTest asserts the
flag is an int.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 18:42:58 +02:00
boban 57a3dd51b6 feat(security): signed confirm-action tokens + airtight backup-code reveal
Two hardening features completed in parallel (each with its own spec + tests),
sharing call sites (TwoFactorSetup, WebauthnKeys, routes) so committed together:

- ConfirmAction token hardening: destructive confirm flows now carry a server-issued,
  single-use, signed token (App\Support\Confirm\ConfirmToken) instead of trusting a
  client-mutable event/params/auditTarget. Every #[On] apply handler consumes + validates
  the token (forged/replayed/direct-bypass calls no-op). Server-scoped per action; closes
  the codebase-wide confirm-bypass + audit-forgery vector across Security, Sessions, Users,
  WebauthnKeys, Servers\Show, Services, System, Files.
- Backup-code airtight reveal: codes are revealed via a transient channel, not a persisted
  Livewire property, so a captured/replayed snapshot can't re-render them; the recovery
  download is grant-gated.

Full suite green (162).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 18:35:33 +02:00
boban 5eb3deff0b docs(spec): 0.9.0 accounts, sessions, auto-restart, SMTP, audit-retention
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 23:17:41 +02:00
boban 19fc6e1f34 docs(spec): external reverse-proxy TLS mode (Zoraxy in front)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 22:26:20 +02:00
boban 3629f18844 docs(spec): auto-provision SSH key & safely disable password login
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 21:54:02 +02:00
boban 1273d8290d docs: implementation plan for optional pluggable 2FA
8 TDD tasks covering the approved spec: pluggable factor semantics, optional
onboarding, recovery-codes modal, factor-adaptive login challenge, independent
TOTP/key management in Settings, key-only-safe forgot-password, plus the R12 +
Codex + release sweep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 20:20:37 +02:00
boban bdec68f661 docs(spec): optional, pluggable 2FA (TOTP and/or YubiKey) design
2FA becomes optional + pluggable: TOTP, a security key, or both, fully
deactivatable, recommended not forced. hasTwoFactorEnabled = either factor;
onboarding drops the forced-2FA redirect; recovery codes become a modal (drop the
dedicated page/route); challenge adapts to whichever factor(s) exist; last-factor
removal clears codes. WebAuthn E2E stays domain-deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 19:47:55 +02:00
boban eef79d1758 docs(plan): WebAuthn/YubiKey 2nd-factor implementation plan
TDD plan: install web-auth/webauthn-lib + credential storage, available() gate,
ceremony options/verify (against the installed v5 API), login-challenge use,
settings register/list/remove + JS, and a verification gate. Real key E2E
deferred to a domain+HTTPS host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 18:16:10 +02:00
boban 27ebc570f4 docs(spec): WebAuthn/YubiKey 2nd-factor design (Phase 2)
Optional security-key 2nd factor via web-auth/webauthn-lib, gated on domain +
HTTPS (rpId = active domain). TOTP + backup codes stay the required 2FA and the
only recovery. Build now with unit/feature tests (mocked ceremony) + R12 gating;
real YubiKey end-to-end deferred to a domain+HTTPS host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 18:12:32 +02:00
boban 63fb1686a4 docs(plan): account recovery implementation plan
TDD plan: recovery-code storage, challenge backup-code acceptance, recovery-codes
view (show/download/regenerate), forgot-password (2FA-code), email reset link
(gated), clusev:reset-admin CLI, and a verification gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 16:47:12 +02:00
boban f0a337c8bd docs(spec): account recovery design (forgot-password, 2FA backup codes, CLI)
Phase 1: 2FA recovery codes (encrypted, show-once + downloadable, usable at the
challenge), a forgot-password flow (2FA-code proof primary, email link only when
SMTP is configured), and a clusev:reset-admin CLI lockout fallback. WebAuthn is
deferred to a Phase-2 spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 16:41:45 +02:00
boban 50b18aa45f docs(plan): server-details UX + hardening polish implementation plan
Eight-task TDD plan covering the seven fixes: button kit, pending status,
auto-updates reframe, FleetService::testConnection, create-verify, panel grid,
SSH hint, and a final R12+Codex gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 10:17:28 +02:00
boban a0bc8d41cb docs(spec): server-details UX + hardening polish design
Design for seven reported fixes: unified bordered button kit (retire
ghost/ghost-danger, add danger-soft), firewall+fail2ban panels in an
installed-only responsive grid, auto-updates reframed as a neutral operator
preference (never an insecure verdict), create-server SSH verification with
atomic rollback, a new "Initialisierung" (pending) server status, and an
SSH key-only hint on the checklist. Includes file map, TDD + R12 plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 10:09:17 +02:00