feat: RBAC, atomic deployments, fleet backups, and licensing (Pro) (#185)

* feat: add RBAC viewer accounts, atomic deployments, and fleet-wide backups (Pro)

Introduces three Pro-tier features:

- RBAC: Multi-user system with admin/viewer roles, user management UI,
  automatic migration from single-admin credentials, viewer restrictions
  across the entire UI (read-only editor, hidden action buttons)

- Atomic Deployments: Pre-deploy file backup to .sencho-backup/, automatic
  rollback on health probe failure, manual rollback button, health probes
  added to stack updates, webhook-triggered deploys use atomic rollback

- Fleet-Wide Backups: Point-in-time snapshots of compose files across all
  nodes (local + remote), stored centrally in SQLite, per-stack restore
  with optional redeploy, graceful handling of offline nodes

* fix(settings): use correct ProGate prop name in UsersSection

* fix(settings): remove unused isPro prop from UsersSection

* fix(auth): fetch user info after login and setup so isAdmin is set correctly

* feat(pricing): revise pricing strategy and enforce variant-based seat limits

Raise Personal Pro from $49/yr to $69/yr with 3 viewer seats (up from 1).
Add $15/mo billing option for Team Pro. Mark lifetime pricing as a
90-day early-adopter offer. Store Lemon Squeezy variant_name on
activation/validation and enforce seat limits server-side per variant.

* feat(licensing): add Lemon Squeezy checkout, webhook, and billing portal integration

Server-side checkout URL generation (POST /api/checkout) with admin email
pre-fill and instance_id custom data. HMAC-SHA256 verified webhook endpoint
(POST /api/webhooks/lemonsqueezy) handling order, subscription, and payment
lifecycle events for automatic license activation. Customer billing portal
link stored from webhook events and exposed via GET /api/billing/portal.
In-app checkout buttons in Settings with manual license key fallback.

* fix(licensing): exempt Lemon Squeezy webhook from auth middleware

The catch-all auth middleware on /api/* was blocking the public webhook
endpoint. Added /webhooks/lemonsqueezy to the exemption list alongside
/auth/* and /webhooks/:id/trigger.

* feat(pricing): update pricing to final live rates

Personal Pro: $7.99/month, $69.99/year, $249 lifetime.
Team Pro: $49.99/month, $499.99/year, $1,499 lifetime.
Added personal_monthly checkout variant across backend, frontend, and website.

* refactor(licensing): remove server-side checkout/webhook for self-hosted model

Sencho is self-hosted — each user runs their own instance, so there is
no central server to receive webhooks or hold the store API key. Replaced
in-app checkout buttons with a "View Pricing" redirect to sencho.io and
kept manual license key activation as the primary flow.

- Delete LemonSqueezyService (checkout, webhook, HMAC verification)
- Remove POST /api/checkout, GET /api/billing/portal, POST /api/webhooks/lemonsqueezy
- Remove raw body parser and auth exemption for webhook route
- Remove all LEMONSQUEEZY_* env vars from .env.example
- Replace checkout buttons in SettingsModal with single "View Pricing" button
- Simplify LicenseContext checkout to open sencho.io pricing page
- Update licensing docs to reflect website-based purchase flow

* chore: normalize em-dashes to hyphens across codebase (linter)

* chore: remove accidentally tracked directories from index
This commit is contained in:
Anso
2026-03-26 21:58:24 -04:00
committed by GitHub
parent 269ea6fe53
commit 32a7d53b2b
54 changed files with 721 additions and 543 deletions
+4 -4
View File
@@ -3,7 +3,7 @@ title: Multi-Node Management
description: Connect multiple Sencho instances and manage all your servers from a single dashboard.
---
Sencho's multi-node feature lets you manage Docker Compose stacks on multiple servers all from the same browser tab. Each server runs its own Sencho instance, and your primary instance acts as a transparent proxy to the others.
Sencho's multi-node feature lets you manage Docker Compose stacks on multiple servers - all from the same browser tab. Each server runs its own Sencho instance, and your primary instance acts as a transparent proxy to the others.
<Frame>
<img src="/images/multi-node/node-manager.png" alt="Node Manager showing a local and a remote node, both Online" />
@@ -21,7 +21,7 @@ Your primary Sencho installation is always listed as **Local**. It is the defaul
### Step 1: Generate a token on the remote machine
On the **remote** Sencho instance (the server you want to add), open **Settings → Nodes** and click **Generate Token**. Copy the generated token you'll only see it once.
On the **remote** Sencho instance (the server you want to add), open **Settings → Nodes** and click **Generate Token**. Copy the generated token - you'll only see it once.
<Note>
The token is a long-lived JWT scoped to `node_proxy`. Anyone with this token can fully control that Sencho instance, so treat it like a password.
@@ -55,7 +55,7 @@ The Compose directory path matters on remote nodes too. When you register a remo
## Switching between nodes
The **node switcher** dropdown in the top-left of the sidebar shows the currently active node. Click it to switch to any registered node. All views dashboard stats, stack list, editor, resources, logs immediately reflect the selected node.
The **node switcher** dropdown in the top-left of the sidebar shows the currently active node. Click it to switch to any registered node. All views - dashboard stats, stack list, editor, resources, logs - immediately reflect the selected node.
<Frame>
<img src="/images/multi-node/node-manager.png" alt="Node switcher dropdown and node list" />
@@ -77,4 +77,4 @@ Click the **pencil icon** on any remote node row to edit its name, URL, or token
- Node tokens grant full control over the remote Sencho instance. Rotate them if compromised via Settings → Nodes → Generate Token (the old token is invalidated).
- Use HTTPS between instances in production to prevent token interception.
- The host console and container exec terminals are blocked for node-proxy tokens interactive shell access always requires a real browser session on that instance.
- The host console and container exec terminals are blocked for node-proxy tokens - interactive shell access always requires a real browser session on that instance.