docs(readme): professional product README — install (sudo ./install.sh), domain/TLS, recovery, optional 2FA
Replaces the dev-oriented readme: no CLAUDE.md/rules.md references, 2FA is optional (not forced), documents the overhauled root installer + the clusev user + recovery. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>feat/v1-foundation
parent
4a3c572a89
commit
9035078ac0
151
README.md
151
README.md
|
|
@ -1,101 +1,106 @@
|
||||||
# Clusev
|
# Clusev
|
||||||
|
|
||||||
Self-hosted control panel to administer a **fleet of Linux servers from one dashboard**,
|
**Self-hosted control panel for a fleet of Linux servers — one dashboard, agentless over SSH.**
|
||||||
agentless over SSH. Clusev is the control-plane (UI + orchestration); it talks to real servers
|
|
||||||
with **phpseclib** (exec + SFTP) — it never reimplements daemons. Security-first: 2FA, encrypted
|
|
||||||
SSH-credential vault, and a full audit log. Multi-server is free and never paywalled.
|
|
||||||
|
|
||||||
> Status: **v1** — dashboard/live metrics, systemd services, SFTP file manager, server details,
|
Clusev is the control plane: a modern, security-first web UI that administers your servers by talking
|
||||||
> auth + 2FA. UI copy is German; meta-docs are English.
|
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.
|
||||||
|
|
||||||
**Stack:** Laravel 13 · Livewire 3 · Tailwind v4 · Reverb (realtime) · Redis · MariaDB · phpseclib3.
|
## Features
|
||||||
Everything runs in Docker. See `CLAUDE.md` for architecture and `rules.md` for the hard conventions.
|
|
||||||
|
|
||||||
---
|
- **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.
|
||||||
|
|
||||||
## Development
|
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).
|
||||||
|
|
||||||
The host needs only Docker (no PHP/Composer/Node). The dev `app` container runs php-fpm + nginx +
|
## Requirements
|
||||||
Vite via supervisor; `reverb`, `queue`, `mariadb`, `redis` are their own services.
|
|
||||||
|
- 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).
|
||||||
|
- Nothing else — the installer sets up Docker for you.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env # then set DB_PASSWORD / DB_ROOT_PASSWORD
|
git clone https://git.bave.dev/boban/clusev.git
|
||||||
docker compose up -d --build # app on :80, Vite HMR on :5173
|
cd clusev
|
||||||
docker compose run --rm --no-deps -u "${HOST_UID:-1002}:${HOST_GID:-1002}" app php artisan key:generate
|
sudo ./install.sh
|
||||||
docker compose exec app php artisan migrate --seed
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Run any tooling inside the container, e.g.
|
The installer is idempotent (safe to re-run) and, in one pass:
|
||||||
`docker compose exec app php artisan make:livewire Servers/Show` (class-based — never Volt).
|
|
||||||
|
|
||||||
---
|
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**, 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 a one-time random password.
|
||||||
|
5. Installs a themed host login banner (MOTD) showing the dashboard address.
|
||||||
|
|
||||||
## Production install
|
When it finishes, the terminal prints a single summary: the **dashboard URL**, the **admin login**
|
||||||
|
(e-mail + one-time password), and the **`clusev` host user** + its password. These passwords are shown
|
||||||
|
**only once** — note them down.
|
||||||
|
|
||||||
One host, only Docker + a sudo user. **Caddy** is the single host-exposed service: auto-TLS when a
|
On first login the panel forces you to set your own password. 2FA is **optional but recommended** —
|
||||||
domain is set, plain HTTP on the bare IP otherwise. `install.sh` is idempotent — it generates
|
enable TOTP and/or a security key from **Settings → Security** whenever you like.
|
||||||
secrets once (never regenerates), brings up the prod stack, migrates, and creates the first admin
|
|
||||||
with a **one-time random password printed only on the terminal**.
|
## 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
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.bave.dev/boban/clusev.git && cd clusev
|
cd clusev
|
||||||
./install.sh
|
git pull
|
||||||
|
sudo ./install.sh # idempotent: rebuilds, restarts and migrates — secrets are preserved
|
||||||
```
|
```
|
||||||
|
|
||||||
Prompts (non-interactive: set `CLUSEV_DOMAIN` / `CLUSEV_ADMIN_EMAIL` in the environment):
|
## Account recovery
|
||||||
|
|
||||||
```
|
The **forgot-password** screen offers self-service recovery: an **e-mail reset link** (valid 15
|
||||||
Domain (empty = access by IP over HTTP): clusev.example.com
|
minutes) when SMTP is configured, or an inline **2FA-proof reset** (e-mail + TOTP or a backup code +
|
||||||
Admin e-mail (login + Let's Encrypt): admin@example.com
|
new password) as a fallback.
|
||||||
```
|
|
||||||
|
|
||||||
The closing banner shows the URL + the one-time admin password. On first login Clusev **forces** a
|
Completely locked out (lost password and 2FA, no SMTP)? Recover from the host:
|
||||||
password change and 2FA enrolment before the panel unlocks.
|
|
||||||
|
|
||||||
### One knob: `APP_DOMAIN`
|
|
||||||
|
|
||||||
| `APP_DOMAIN` | proxy | URL | Reverb |
|
|
||||||
|---|---|---|---|
|
|
||||||
| *(empty)* | Caddy serves plain HTTP on `APP_PORT` | `http://<ip>` | `ws://<ip>/app/*` |
|
|
||||||
| `clusev.example.com` | Caddy gets a Let's Encrypt cert, forces HTTPS | `https://…` | `wss://…/app/*` |
|
|
||||||
|
|
||||||
`SITE_ADDRESS`, `APP_URL`, `REVERB_*` are **derived** from it by the installer — nothing hardcoded.
|
|
||||||
Bare-IP mode serves 2FA/audit over cleartext HTTP; the installer warns loudly. Let's Encrypt needs
|
|
||||||
publicly reachable 80/443 — a private (RFC1918) target needs a DNS-01 Caddy build instead.
|
|
||||||
|
|
||||||
### Manual deploy (for operators who don't `curl | bash`)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.prod.yml build
|
|
||||||
docker compose -f docker-compose.prod.yml up -d
|
|
||||||
docker compose -f docker-compose.prod.yml exec -u app app php artisan migrate --force
|
|
||||||
docker compose -f docker-compose.prod.yml exec -u app app php artisan clusev:install --email=admin@example.com
|
|
||||||
```
|
|
||||||
|
|
||||||
> In-dashboard updates (signed intent file + host-side updater, digest-pinned, cosign-verified,
|
|
||||||
> 2FA-gated, with backup + rollback) are designed in `docs/install-update-design.md` and land in
|
|
||||||
> v1.x — they are intentionally **not** shipped yet.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Account recovery / Wiederherstellung
|
|
||||||
|
|
||||||
Forgot the admin password? The panel offers two self-service paths on the **forgot-password**
|
|
||||||
screen: an **e-mail reset link** (15 minutes valid) when SMTP is configured, and an inline
|
|
||||||
**2FA-proof reset** (e-mail + TOTP or a backup code + new password) as a fallback.
|
|
||||||
|
|
||||||
Locked out with **no 2FA and no SMTP**? As a last resort, SSH to the host and reset the admin
|
|
||||||
from the container:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cd clusev
|
||||||
docker compose -f docker-compose.prod.yml exec app php artisan clusev:reset-admin
|
docker compose -f docker-compose.prod.yml exec app php artisan clusev:reset-admin
|
||||||
```
|
```
|
||||||
|
|
||||||
This recovery command is documented in-panel under **Settings → Security** (admin-only) — it is
|
This clears the second factor so you can set a new password on the next login. The bare-IP
|
||||||
deliberately not advertised on the public, pre-login forgot-password screen.
|
`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
|
## License
|
||||||
|
|
||||||
AGPL core + commercial Pro modules (open-core). Multi-server fleet management is always free.
|
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>.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue