Go to file
boban c020af4c09 fix(webauthn): add security-key hint so passkey managers defer
Even with authenticatorAttachment=cross-platform, Bitwarden's extension
intercepted navigator.credentials.create and offered to save a passkey. Add the
WebAuthn L3 'hints: [security-key]' to both registration and assertion options;
browsers and passkey managers that honor hints step aside and let the hardware
key prompt through. Injected into the client payload only (the session copy used
for server validation is unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:30:52 +02:00
app fix(webauthn): add security-key hint so passkey managers defer 2026-06-19 21:30:52 +02:00
bootstrap feat(sessions): database sessions + list/revoke (other devices, per-user, global) with remember-token rotation 2026-06-14 23:34:00 +02:00
config fix(webauthn): add security-key hint so passkey managers defer 2026-06-19 21:30:52 +02:00
database fix(security): harden brute-force, rate-limiting and auth-DoS (audit follow-up) 2026-06-17 17:59:45 +02:00
docker fix(tls): reverb WebSocket + honest TLS status behind an external proxy 2026-06-19 20:49:25 +02:00
docs docs(spec): dashboard "request certificate" button (trigger Caddy on-demand TLS) 2026-06-17 18:24:25 +02:00
lang fix(tls): reverb WebSocket + honest TLS status behind an external proxy 2026-06-19 20:49:25 +02:00
public feat(branding): favicons + PWA manifest, and custom branded error pages 2026-06-14 15:27:49 +02:00
resources fix(tls): reverb WebSocket + honest TLS status behind an external proxy 2026-06-19 20:49:25 +02:00
routes fix(security): tighten recovery-download grant window (reject stale/non-int/future) 2026-06-15 18:36:31 +02:00
storage feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
tests fix(webauthn): add security-key hint so passkey managers defer 2026-06-19 21:30:52 +02:00
.dockerignore feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
.editorconfig feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
.env.example feat(versions): update completion feedback, instant availability, local time 2026-06-19 19:08:54 +02:00
.gitattributes feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
.gitignore feat(domain): change the panel domain from the dashboard (v0.4.0) 2026-06-14 01:38:16 +02:00
CHANGELOG.md fix(webauthn): add security-key hint so passkey managers defer 2026-06-19 21:30:52 +02:00
CLAUDE.md fix(blade): garbled header on every signed-in page + private metrics channel (v0.4.1) 2026-06-14 08:34:07 +02:00
Dockerfile feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
README.md feat(deploy): one-command update.sh + preserve config on install re-run 2026-06-19 17:58:50 +02:00
artisan feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
composer.json feat(webauthn): install web-auth/webauthn-lib + credential storage 2026-06-14 18:18:05 +02:00
composer.lock feat(webauthn): install web-auth/webauthn-lib + credential storage 2026-06-14 18:18:05 +02:00
docker-compose.prod.yml fix(deploy): apply Caddyfile changes on update (dir mount + recreate) 2026-06-19 20:57:42 +02:00
docker-compose.yml feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
handoff.md feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
install.sh fix(deploy): apply Caddyfile changes on update (dir mount + recreate) 2026-06-19 20:57:42 +02:00
kickoff-prompt.md chore: project bootstrap — rules.md, CLAUDE.md, .gitignore 2026-06-11 23:12:58 +02:00
package-lock.json feat: live metrics over Reverb (mock emitter) 2026-06-12 01:23:10 +02:00
package.json feat: live metrics over Reverb (mock emitter) 2026-06-12 01:23:10 +02:00
phpunit.xml feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00
rules.md fix(blade): garbled header on every signed-in page + private metrics channel (v0.4.1) 2026-06-14 08:34:07 +02:00
update.sh feat(deploy): one-command update.sh + preserve config on install re-run 2026-06-19 17:58:50 +02:00
vite.config.js feat: scaffold — Dockerized Laravel 13 + Livewire 3 + Tailwind 4 + Reverb 2026-06-12 00:31:50 +02:00

README.md

Clusev

Self-hosted control panel for a fleet of Linux servers — one dashboard, agentless over SSH.

Clusev is the control plane: a modern, security-first web UI that administers your servers by talking to them directly over SSH (exec + SFTP via phpseclib). It installs nothing on the target machines and never reimplements their daemons. Multi-server management is free and never paywalled.

Features

  • Dashboard & live metrics — CPU, memory, load and disk per server, streamed in real time.
  • systemd services — list, start/stop/restart, live journal tail.
  • SFTP file manager — browse, edit text files, preview images.
  • Server details & hardening — resource gauges, volumes, interfaces, SSH keys; UFW/firewalld rules and fail2ban status with one-click controls; and a one-click "generate an SSH key and disable password login, safely" flow.
  • Security — optional, pluggable 2FA (TOTP and/or hardware security keys, or off), one-time backup codes, an encrypted SSH-credential vault, and a complete, tamper-evident audit log.
  • Multiple administrators — add further admin accounts; every action is attributed in the audit log; view and revoke active sessions (per device, per user, or globally).
  • Domain, TLS & e-mail — run on a bare IP over HTTP, set a domain for automatic HTTPS, or put your own reverse proxy in front. Configure SMTP in-panel for password-reset e-mails.

The panel is built on Laravel 13, Livewire 3, Tailwind v4, Laravel Reverb (realtime), Redis, MariaDB and phpseclib — all running in Docker. The interface is in German (English available).

Requirements

  • A Debian or Ubuntu server (a small VM is enough) with root access.
  • A public IP. Optionally a domain whose DNS points at the server (for automatic HTTPS).
  • Only git to fetch the repo (one line below) — the installer sets up Docker and everything else.

Install

sudo apt-get update && sudo apt-get install -y git   # minimal images often lack git
git clone https://git.bave.dev/boban/clusev.git
cd clusev
sudo ./install.sh

The installer is idempotent (safe to re-run) and, in one pass:

  1. Installs Docker (Debian/Ubuntu, from Docker's official repository) if it isn't already present.
  2. Creates a dedicated clusev system user — in the docker group, owning and running the stack — with a random password.
  3. Asks for a domain (leave empty for IP access) and an admin e-mail (leave empty for the default admin@clusev.local), or reads CLUSEV_DOMAIN / CLUSEV_ADMIN_EMAIL from the environment for an unattended install. If you give a domain it checks whether DNS already points here: if so a certificate is obtained automatically; if not, it warns you and lets you take the domain anyway (HTTP until DNS is correct) or continue on the IP.
  4. Generates all secrets (once — never regenerated on re-run), builds the image, starts the stack, runs the migrations, and creates the first administrator with the default password clusev (you are forced to change it on first login).
  5. Installs a themed host login banner (MOTD) showing the dashboard address and live stack status.

When it finishes, the terminal prints a single summary: the dashboard URL, the admin login (your e-mail — admin@clusev.local if you left it empty — plus the default password clusev), and the clusev host user + its random password (shown only once — note it down).

Log in with your admin e-mail (default admin@clusev.local) and the password clusev — the panel then forces you to set a new password immediately. Do this right away: clusev is a known default, so the account is only safe once you've changed it. You can change the login e-mail later under Settings → Profile. 2FA is optional but recommended — enable TOTP and/or a security key from Settings → Security whenever you like.

Access, domain & TLS

  • Bare IP (no domain): served over plain HTTP at http://<server-ip>. This address always stays reachable as a recovery path, even after a domain is configured.
  • With a domain: set it during install or later under System → Domain & TLS. The panel obtains and renews a Let's Encrypt certificate automatically and serves HTTPS — just point DNS at the server. (Let's Encrypt needs publicly reachable ports 80/443.)
  • Behind your own reverse proxy (one that already terminates TLS): switch TLS-Terminierung to Externer Reverse-Proxy in System → Domain & TLS. The panel then serves HTTP only and trusts the proxy's forwarded scheme — set TRUSTED_PROXY_CIDR to the proxy's address and firewall the HTTP port so only the proxy can reach it.

Domain/TLS changes apply on a stack restart — use the "Jetzt neu starten" button in System (no terminal needed; a small, scoped host service performs the restart).

Updating

cd clusev
sudo ./update.sh         # pulls the latest code, then rebuilds, restarts and migrates

update.sh fast-forwards the repo (it never discards local changes), re-runs the idempotent installer non-interactively, and updates itself if the script changed. Secrets and the configured domain / e-mail are preserved. (The older two-step git pull && sudo ./install.sh still works.)

Account recovery

The forgot-password screen offers self-service recovery: an e-mail reset link (valid 15 minutes) when SMTP is configured, or an inline 2FA-proof reset (e-mail + TOTP or a backup code + new password) as a fallback.

Completely locked out (lost password and 2FA, no SMTP)? Recover from the host:

cd clusev
docker compose -f docker-compose.prod.yml exec app php artisan clusev:reset-admin

This clears the second factor so you can set a new password on the next login. The bare-IP http://<server-ip> address is also always available if a domain becomes unreachable. (This command is documented in-panel under Settings → Security and is deliberately not shown on the public forgot-password screen.)

License

Open core, AGPL-3.0 — multi-server fleet management is always free; optional Pro modules (SSO/LDAP, RBAC, audit export, alerting) are separate. Project home: https://git.bave.dev/boban/clusev.