102 lines
4.2 KiB
Markdown
102 lines
4.2 KiB
Markdown
# Clusev
|
|
|
|
Self-hosted control panel to administer a **fleet of Linux servers from one dashboard**,
|
|
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,
|
|
> auth + 2FA. UI copy is German; meta-docs are English.
|
|
|
|
**Stack:** Laravel 13 · Livewire 3 · Tailwind v4 · Reverb (realtime) · Redis · MariaDB · phpseclib3.
|
|
Everything runs in Docker. See `CLAUDE.md` for architecture and `rules.md` for the hard conventions.
|
|
|
|
---
|
|
|
|
## Development
|
|
|
|
The host needs only Docker (no PHP/Composer/Node). The dev `app` container runs php-fpm + nginx +
|
|
Vite via supervisor; `reverb`, `queue`, `mariadb`, `redis` are their own services.
|
|
|
|
```bash
|
|
cp .env.example .env # then set DB_PASSWORD / DB_ROOT_PASSWORD
|
|
docker compose up -d --build # app on :80, Vite HMR on :5173
|
|
docker compose run --rm --no-deps -u "${HOST_UID:-1002}:${HOST_GID:-1002}" app php artisan key:generate
|
|
docker compose exec app php artisan migrate --seed
|
|
```
|
|
|
|
Run any tooling inside the container, e.g.
|
|
`docker compose exec app php artisan make:livewire Servers/Show` (class-based — never Volt).
|
|
|
|
---
|
|
|
|
## Production install
|
|
|
|
One host, only Docker + a sudo user. **Caddy** is the single host-exposed service: auto-TLS when a
|
|
domain is set, plain HTTP on the bare IP otherwise. `install.sh` is idempotent — it generates
|
|
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**.
|
|
|
|
```bash
|
|
git clone https://git.bave.dev/boban/clusev.git && cd clusev
|
|
./install.sh
|
|
```
|
|
|
|
Prompts (non-interactive: set `CLUSEV_DOMAIN` / `CLUSEV_ADMIN_EMAIL` in the environment):
|
|
|
|
```
|
|
Domain (empty = access by IP over HTTP): clusev.example.com
|
|
Admin e-mail (login + Let's Encrypt): admin@example.com
|
|
```
|
|
|
|
The closing banner shows the URL + the one-time admin password. On first login Clusev **forces** a
|
|
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
|
|
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
|
|
deliberately not advertised on the public, pre-login forgot-password screen.
|
|
|
|
---
|
|
|
|
## License
|
|
|
|
AGPL core + commercial Pro modules (open-core). Multi-server fleet management is always free.
|