The README banner showed v0.9.40 while the badge showed v0.9.58 — static fields
that go stale and disagree. Removed the per-release version from both the badge row
and the banner SVG (the release version lives in tags/changelog, not a frozen
image). Added a "Supported operating systems" matrix (Debian/Ubuntu · RHEL · SUSE
full; Arch partial; Alpine metrics-only; anything else metrics-only) and real
minimum requirements (CPU/RAM/disk for the panel host; nothing on managed servers).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The actual file changes for the leak-safety hardening described in a94efb0
(that commit only captured the art/ asset move):
- promote.sh: leak guard refusing to publish a tree carrying a private identifier
(git.bave.dev / boban/clusev / clusev-staging) or an internal doc; negative test.
- .gitattributes: export-ignore kickoff-prompt.md (leaked the VM IP).
- README: reference the moved art/ SVGs (docs/ is export-ignored).
- .env.example: comment out CLUSEV_REPOSITORY so the public-repo config default
applies (a present-but-empty value would disable the update check).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The release-pipeline + one-time-wiring section named the private staging runner
label and described internal setup — not public-facing. Move it to
docs/release-pipeline.md (export-ignore, never published) so the public README
stays clean and no private repo name leaks via the published tree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Default update source, README (badge/clone/home), the console + source-view
branding, and the CHANGELOG compare links now reference the public repo. Private
URLs remain only in the gitignored .env (install.sh derives CLUSEV_REPOSITORY
from the clone origin). Bump 0.9.58.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- promote.sh: fail fast before mutating the public tree if the tag was already
promoted (avoids a half-commit left on the runner on re-dispatch).
- ci-staging.yml: pass STAGING_PROJECT_DIR via env + :? guard instead of a
${{ }} expression in run: (clears the template-injection lint, fails loudly
on misconfig).
- README: PUBLIC_REPO_URL was never read by any workflow — keep only the slug.
- set-repository-url.test.sh: chmod +x for mode parity with promote.test.sh.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebuild the README around the product's "Tactical Terminal" design system
(dark graphite, signal-orange, cyan, mono type) so it reads as a professional,
self-hosted enterprise product instead of a plain text file:
- docs/assets/clusev-banner.svg — hero banner with the Clusev wordmark, tagline,
brand chips, and a live "fleet" node-graph (control-plane hub → server cards
with the online/warning/offline status triad + CPU bars and animated data
pulses). Self-contained: the real product fonts (Chakra Petch + JetBrains Mono)
are embedded base64 so Gitea's <img>-rendered SVG shows pixel-correct type.
- docs/assets/clusev-architecture.svg — Operator → Control plane → Fleet diagram
(HTTPS/WS · Reverb in, SSH+SFTP · phpseclib out), same brand language.
- README.md — centered hero, brand-matched shields badges, anchor nav, a
"why" three-up, a Features table, the architecture diagram + a tech-stack
table, and the existing install / domain-TLS / updating / security-recovery
content kept verbatim (now with a WireGuard feature row). All graphics are
external SVG files (Gitea strips inline <svg>); relative paths resolve on the
default branch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add update.sh (sudo ./update.sh): git pull --ff-only (never discards local
changes), self-update via re-exec if the script itself changed, then hand off
to the idempotent install.sh non-interactively. Sets safe.directory so a
root-run pull works on the clusev-owned tree.
Fix install.sh so a non-interactive re-run (an update) no longer wipes a
configured domain / ACME e-mail: both now default to the existing .env value
when not supplied. First install (empty = bare IP) is unchanged.
README documents 'sudo ./update.sh'; the MOTD shows the update command.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Default the first-admin login e-mail to a predictable admin@clusev.local
when no --email / CLUSEV_ADMIN_EMAIL is given, instead of a host-derived
admin@<host> address the operator would have to guess. Combined with the
default password 'clusev' (forced change on first login), a fresh install
now has a fully predictable initial login. Override at install time or
change it later under Settings -> Profile.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Initial admin now uses the standard default password "clusev" (operator decision) with
must_change_password forcing a new password on first login; banner/MOTD/README tell the
operator to change it immediately. Idempotent no-op when an admin already exists.
- Richer host MOTD (CLUSEV wordmark, version, live stack status, login + management hints).
- README: install block now installs git (minimal Debian/Ubuntu lack it) and lists it as a
prerequisite — found by a full fresh-VM install test (install + idempotent re-run verified).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Validated install.sh end-to-end on a fresh Debian 13 VM (Docker apt install, clusev
user, image build, stack up, migrate, admin, MOTD) and a fully idempotent re-run.
Two follow-ups from that test:
- MOTD: replace the bare two-liner with an orange CLUSEV wordmark, the version, a LIVE
stack status ("X/Y Dienste aktiv", green/red via `docker compose ps` at each login),
the dashboard URL, a login hint (where the one-time credentials come from — no secret),
and management commands. install.sh substitutes __CLUSEV_URL__, __CLUSEV_VERSION__ and
__CLUSEV_COMPOSE__; the status lookup degrades to "unbekannt" and never fails a login.
- README: minimal Debian/Ubuntu images ship without `git`, so the documented `git clone`
failed before reaching the installer. The install block now installs git first and the
requirements note it. (curl is missing too but the installer self-installs it.)
Admin password strategy confirmed correct as-is: clusev:install generates a random
20-char password with must_change_password=true (forced rotation on first login) — kept
over a guessable default like "clusev", which would be a default-credential vuln and
would nullify the brute-force hardening.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
One-command, idempotent production install per docs/install-update-design.md
(the "JETZT" phase). Onboarding (forced password change + 2FA) already ships
via EnsureSecurityOnboarded; the in-dashboard self-updater stays deferred to v1.x.
- install.sh: preflight -> idempotent secrets (set_kv never regenerates) ->
derive SITE_ADDRESS/APP_URL/REVERB_* from the single APP_DOMAIN knob -> build ->
up -> wait DB -> migrate + cache -> clusev:install. One-time admin password is
printed only on the terminal, never stored. Bare-IP and private-IP warnings.
- app/Console/Commands/Install.php: idempotent first admin (Str::password(20),
must_change_password); hard no-op once a user exists (INSTALL_LOCK idiom).
- docker/caddy/Caddyfile: one template, both modes; Reverb wss over /app/* and
/apps/* on the same address; admin-API-free, security headers. Validated for
bare-IP (:80) and domain (https) modes.
- docker-compose.prod.yml: Caddy is the only host-published service (80/443 +
HTTP/3); app/reverb/queue/mariadb lose host ports (internal net only);
CLUSEV_IMAGE indirection (locally-built tag, GHCR digest later); Redis requires
a password; caddy-data/caddy-config volumes persist ACME certs.
- .env.example: APP_DOMAIN/APP_SCHEME/ACME_EMAIL/SITE_ADDRESS/CLUSEV_IMAGE/
UPDATE_HMAC_KEY documented.
- README: real Clusev install/deploy guide (replaces Laravel boilerplate).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>