diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..018d3f8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,65 @@ +name: Build & Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + release: + strategy: + fail-fast: false + matrix: + include: + - platform: ubuntu-22.04 + args: "" + - platform: windows-latest + args: "" + - platform: macos-latest + args: "--target universal-apple-darwin" + + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install frontend dependencies + run: npm ci + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - name: Install Linux dependencies + if: matrix.platform == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + + - name: Build and release + uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tagName: ${{ github.ref_name }} + releaseName: "Clustri ${{ github.ref_name }}" + releaseBody: "See the assets below to download and install." + releaseDraft: true + prerelease: false + updaterJsonPreferNsis: true + args: ${{ matrix.args }} diff --git a/site/src/components/AppWindowMockup.astro b/site/src/components/AppWindowMockup.astro index 79bf471..36e7679 100644 --- a/site/src/components/AppWindowMockup.astro +++ b/site/src/components/AppWindowMockup.astro @@ -4,7 +4,7 @@ const navItems = [ { label: 'Dashboard', icon: 'grid', active: true }, { label: 'Nodes', icon: 'server', active: false }, { label: 'VMs', icon: 'box', active: false }, - { label: 'Containers', icon: 'box', active: false }, + { label: 'CTs', icon: 'box', active: false }, { label: 'Storage', icon: 'hdd', active: false }, { label: 'Tasks', icon: 'todo', active: false }, { label: 'Backups', icon: 'shield', active: false }, @@ -110,7 +110,7 @@ const quickActions = [
-

Operating on a fallback node

+

Using a fallback node

https://192.168.1.12:8006

@@ -146,7 +146,7 @@ const quickActions = [

Offline

-
+
{clusterNodes.map((n) => (
@@ -219,7 +219,7 @@ const quickActions = [
-

Task activity

+

Recent tasks

diff --git a/site/src/components/ArchitectureStrip.astro b/site/src/components/ArchitectureStrip.astro index a7b8715..1e94a86 100644 --- a/site/src/components/ArchitectureStrip.astro +++ b/site/src/components/ArchitectureStrip.astro @@ -5,8 +5,8 @@ import DotMatrixText from './DotMatrixText.astro'
-

One proxy between you and the API.

-

The frontend calls Rust commands over Tauri IPC. The backend proxies them to the Proxmox API over HTTPS.

+

How Clustri reaches Proxmox.

+

The React frontend calls Rust commands through Tauri IPC. The Rust backend sends each request to Proxmox over HTTPS.

@@ -43,7 +43,7 @@ import DotMatrixText from './DotMatrixText.astro'
-

Ordered, deduped endpoints: primary → fallbacks → discovered nodes. Connect / timeout / DNS / transport failure → retry next endpoint.

+

Clustri keeps an ordered list of unique endpoints: primary, fallbacks, then discovered nodes. Connection, timeout, DNS, or transport errors trigger the next attempt.

connected failover diff --git a/site/src/components/CommandPalette.astro b/site/src/components/CommandPalette.astro index 7aa1d0e..4f979e1 100644 --- a/site/src/components/CommandPalette.astro +++ b/site/src/components/CommandPalette.astro @@ -5,12 +5,12 @@ import DotMatrixText from './DotMatrixText.astro'
- +
- +
diff --git a/site/src/components/DownloadCta.astro b/site/src/components/DownloadCta.astro index 281cc89..9cefd03 100644 --- a/site/src/components/DownloadCta.astro +++ b/site/src/components/DownloadCta.astro @@ -4,9 +4,9 @@ import DotMatrixText from './DotMatrixText.astro'
-

Coming soon.

+

Packages are not published yet.

- Clustri is in active development. Packages for Linux, macOS, and Windows are on the way. + Clustri is under active development. Linux, macOS, and Windows packages are next.

@@ -15,8 +15,8 @@ import DotMatrixText from './DotMatrixText.astro'
Linux diff --git a/site/src/components/FailoverPlayground.astro b/site/src/components/FailoverPlayground.astro index 24a09c1..e972951 100644 --- a/site/src/components/FailoverPlayground.astro +++ b/site/src/components/FailoverPlayground.astro @@ -9,28 +9,28 @@ import DotMatrixText from './DotMatrixText.astro'

- One node dies.
You don't notice. + When a node drops,
requests keep moving.

- Requests ride an ordered endpoint list: primary, fallbacks, then discovered cluster nodes. Kill a node on the right — watch the packets reroute and the status flip. + Clustri tries the primary endpoint, then configured fallbacks and discovered cluster nodes. Stop a node on the right to watch the request move.

- - - - + + + +
connected failover failed - — tap a node or use the buttons + Click a node or use a button
- How it works: deduped order primary → fallbacks → discovered. On connect / timeout / DNS / transport failure, retry the next endpoint. Status reflects which endpoint actually answered. + Request order: primary → fallbacks → discovered. A connection, timeout, DNS, or transport failure moves the request to the next endpoint.
@@ -42,7 +42,7 @@ import DotMatrixText from './DotMatrixText.astro'
-

request → endpoints[0] → retry on failure

+

request → primary endpoint → retry if needed

Connected
@@ -94,7 +94,7 @@ import DotMatrixText from './DotMatrixText.astro'
- status: connected — pve1 answered + status: connected, pve1 answered ~18 ms
@@ -103,7 +103,7 @@ import DotMatrixText from './DotMatrixText.astro'
-

Click a node row to kill / heal it. Packets reroute automatically.

+

Click a node row to stop or restore it. Requests move to the next endpoint.

@@ -172,7 +172,7 @@ import DotMatrixText from './DotMatrixText.astro' if (failed) { chip.textContent = 'Failed' chip.className = 'rounded border border-destructive/30 bg-destructive/10 px-1.5 py-0.5 font-mono text-[10px] uppercase text-destructive' - statusText.textContent = 'status: failed — no endpoint reachable' + statusText.textContent = 'status: failed, no endpoint reachable' statusText.className = 'font-mono text-[11px] tabular-nums text-destructive' topRule.className = 'h-px w-full bg-destructive/40' bar.style.width = '100%' @@ -181,7 +181,7 @@ import DotMatrixText from './DotMatrixText.astro' } else if (failover) { chip.textContent = 'Failover' chip.className = 'rounded border border-warning/30 bg-warning/10 px-1.5 py-0.5 font-mono text-[10px] uppercase text-warning' - statusText.textContent = `status: failover — ${names[ai]} answered (${addrs[ai]})` + statusText.textContent = `status: failover, ${names[ai]} answered (${addrs[ai]})` statusText.className = 'font-mono text-[11px] tabular-nums text-warning' topRule.className = 'h-px w-full bg-warning/40' bar.style.width = ai === 1 ? '56%' : '88%' @@ -190,7 +190,7 @@ import DotMatrixText from './DotMatrixText.astro' } else { chip.textContent = 'Connected' chip.className = 'rounded border border-success/25 bg-success/10 px-1.5 py-0.5 font-mono text-[10px] uppercase text-success' - statusText.textContent = `status: connected — ${names[0]} answered` + statusText.textContent = `status: connected, ${names[0]} answered` statusText.className = 'font-mono text-[11px] tabular-nums text-success' topRule.className = 'h-px w-full bg-primary/30' bar.style.width = '18%' diff --git a/site/src/components/FeatureBlocks.astro b/site/src/components/FeatureBlocks.astro index 0dbc2ac..bd12dad 100644 --- a/site/src/components/FeatureBlocks.astro +++ b/site/src/components/FeatureBlocks.astro @@ -8,15 +8,15 @@ import DotMatrixText from './DotMatrixText.astro'

- Your keys stay
in your pocket. + Keep credentials
on your machine.

- API tokens live in the OS keyring — never on disk. Self-signed certs are pinned by SHA-256 on first connect and verified every time after. + Clustri stores API tokens in the OS keyring, not in a file. It pins self-signed certificates by SHA-256 on first connect and checks them on later connections.

-
Token mode: PVEAPIToken / PBSAPIToken header
-
Password mode: ticket auth + CSRFPreventionToken
-
Fallbacks skip TOFU — intentional for home-lab failover; primary is always pinned.
+
Token mode: PVEAPIToken / PBSAPIToken header
+
Password mode: ticket auth + CSRFPreventionToken
+
Fallbacks skip TOFU during home-lab failover; the primary endpoint stays pinned.
@@ -24,7 +24,7 @@ import DotMatrixText from './DotMatrixText.astro'
- SHA-256 fingerprint — pinned + SHA-256 fingerprint: pinned Pinned

A3:0F:71:C9:52:B8:4D:E0:11:2A:7F:9C:63:D4:18:0B

@@ -33,7 +33,7 @@ import DotMatrixText from './DotMatrixText.astro' rustls · sha2 · x509-cert no telemetry
-

Captured on first connect. Verified on every connect after. “Accept anyway” per-connection when you rotate certs.

+

Clustri captures the fingerprint on first connect and checks it later. Accept a rotated certificate for one connection.

@@ -55,18 +55,18 @@ import DotMatrixText from './DotMatrixText.astro'
- +

- Proxmox is powerful.
- The web UI is not the point of failure. + The cluster keeps running.
+ Your management session should stay with it.

- Clustri sits between you and the API. When a node drops, the app retries the next one. You keep working. The cluster keeps running. + Clustri sends requests through the Proxmox API. If a node stops answering, it tries the next endpoint and shows you which node answered.

@@ -79,9 +79,9 @@ import DotMatrixText from './DotMatrixText.astro'
-

Type like you're there.

+

Open a console on any guest.

- noVNC for VMs, xterm.js for containers. Fullscreen, keyboard passthrough, and Ctrl+Alt+Del where you expect it. + Use noVNC for VMs and xterm.js for containers. Fullscreen mode and keyboard passthrough keep the session in the app.

Fullscreen @@ -118,10 +118,10 @@ import DotMatrixText from './DotMatrixText.astro'
-

Built for the people who keep the lights on.

+

Controls for the work you repeat.

- Tasks stream over WebSocket with polling fallback. opens the palette. The tray keeps one-click actions one click away. + WebSocket events update task status. If events stop, polling takes over. Press to find a command. The tray keeps common actions close.

@@ -132,7 +132,7 @@ import DotMatrixText from './DotMatrixText.astro'
WebSocket - Live task & event stream + Live task and event updates
Tray menu diff --git a/site/src/components/Footer.astro b/site/src/components/Footer.astro index abd4573..498942c 100644 --- a/site/src/components/Footer.astro +++ b/site/src/components/Footer.astro @@ -14,26 +14,26 @@ const year = new Date().getFullYear()
-

v0.1.0 · MIT · Not affiliated with Proxmox.

+

v0.1.0 · MIT · Desktop client for Proxmox.

@@ -41,7 +41,7 @@ const year = new Date().getFullYear()

© {year} Clustri. MIT license.

-

Press ⌘K anywhere

+

Press ⌘K to search

diff --git a/site/src/components/Hero.astro b/site/src/components/Hero.astro index 88c5bde..05e4175 100644 --- a/site/src/components/Hero.astro +++ b/site/src/components/Hero.astro @@ -17,17 +17,17 @@ import DotMatrixText from './DotMatrixText.astro'

- Your cluster,
- not your problem. + Manage your cluster,
+ from one desktop app.

- Clustri is a cross-platform desktop console for Proxmox VE. Manage nodes, VMs, storage and backups — and keep working when a node goes down. + Clustri connects to Proxmox VE and PBS so you can manage nodes, VMs, containers, storage, backups, and consoles without keeping a browser tab open.

@@ -42,7 +42,7 @@ import DotMatrixText from './DotMatrixText.astro'
-

Live demo — values tick, tasks stream. The real app looks like this.

+

Dashboard preview with live metrics and task updates.

diff --git a/site/src/components/Nav.astro b/site/src/components/Nav.astro index 9558a3e..6898c17 100644 --- a/site/src/components/Nav.astro +++ b/site/src/components/Nav.astro @@ -25,10 +25,10 @@ import DotMatrixText from './DotMatrixText.astro'
- - Get Clustri + See releases
diff --git a/site/src/components/ScreenshotTour.astro b/site/src/components/ScreenshotTour.astro index d475e6d..42db693 100644 --- a/site/src/components/ScreenshotTour.astro +++ b/site/src/components/ScreenshotTour.astro @@ -7,10 +7,10 @@ import DotMatrixText from './DotMatrixText.astro'

- Feels like the Proxmox UI,
without the browser. + Manage Proxmox resources,
in a desktop window.

- Native window, keyboard-first, no electron bloat. Tabs switch instantly — the data underneath stays live via WebSocket or 10s polling. + Clustri runs as a native Tauri app. Switch between VMs, consoles, and backups while WebSocket events or 10-second polling refreshes the data.

@@ -30,7 +30,7 @@ import DotMatrixText from './DotMatrixText.astro'
-

Virtual Machines — 4 VMs across the cluster

+

Virtual Machines: 4 VMs across the cluster

@@ -41,15 +41,15 @@ import DotMatrixText from './DotMatrixText.astro'
-

Filter by node, type, status. Start / stop / migrate / console in one click. QEMU and LXC.

+

Filter by node, type, or status. Start, stop, migrate, or open a console for QEMU and LXC guests.

-

noVNC for VMs, xterm.js for CTs. Fullscreen, resize, Ctrl+Alt+Del passthrough.

+

Use noVNC for VMs and xterm.js for CTs, with fullscreen, resize, and Ctrl+Alt+Del passthrough.

-

Jobs, groups, snapshots. Verify, prune (dry-run), GC, archive download via OS dialog.

+

Review jobs, groups, and snapshots. Verify archives, preview prune, run GC, or download through the OS dialog.

@@ -60,9 +60,9 @@ import DotMatrixText from './DotMatrixText.astro' const label = document.getElementById('tour-frame-label') const tabs = document.querySelectorAll('[data-tour-tab]') const sources: Record = { - vms: { src: '/screenshots/vms.png', label: 'Virtual Machines — 4 VMs across the cluster', alt: 'Clustri VM list showing running and stopped VMs' }, - console: { src: '/screenshots/console.png', label: 'VNC Console — web-01 (QEMU) on pve1', alt: 'Clustri console view with VNC viewer' }, - backups: { src: '/screenshots/backups.png', label: 'Backups — datastores, groups and snapshots', alt: 'Clustri backups view with datastores and prune controls' }, + vms: { src: '/screenshots/vms.png', label: 'Virtual Machines: 4 VMs across the cluster', alt: 'Clustri VM list showing running and stopped VMs' }, + console: { src: '/screenshots/console.png', label: 'VNC Console: web-01 (QEMU) on pve1', alt: 'Clustri console view with VNC viewer' }, + backups: { src: '/screenshots/backups.png', label: 'Backups: datastores, groups, and snapshots', alt: 'Clustri backups view with datastores and prune controls' }, } tabs.forEach((btn) => { btn.addEventListener('click', () => { diff --git a/site/src/components/StatsStrip.astro b/site/src/components/StatsStrip.astro index 5bb2e69..7d0dc59 100644 --- a/site/src/components/StatsStrip.astro +++ b/site/src/components/StatsStrip.astro @@ -1,18 +1,18 @@ --- import DotMatrixText from './DotMatrixText.astro' --- -
+
0 - ships with the app + telemetry disabled
0 - talks to your nodes + runs against your infrastructure
0 diff --git a/site/src/layouts/Base.astro b/site/src/layouts/Base.astro index 879c894..dd6be69 100644 --- a/site/src/layouts/Base.astro +++ b/site/src/layouts/Base.astro @@ -4,7 +4,7 @@ import '@fontsource-variable/jetbrains-mono' import '../styles/global.css' interface Props { title?: string; description?: string } -const { title = 'Clustri', description = 'A desktop client for managing Proxmox VE servers and clusters.' } = Astro.props +const { title = 'Clustri', description = 'A cross-platform desktop client for managing Proxmox VE and PBS servers and clusters.' } = Astro.props --- diff --git a/site/src/pages/index.astro b/site/src/pages/index.astro index d6105e5..fe05151 100644 --- a/site/src/pages/index.astro +++ b/site/src/pages/index.astro @@ -13,8 +13,8 @@ import CommandPalette from '../components/CommandPalette.astro' ---