Endstate GUI is the official desktop app for Endstate — set up a new Windows PC by reinstalling your apps and restoring your settings from one portable file. Free and open source. It gives you a graphical interface to scan your current machine, save your apps and settings, and restore them on a fresh Windows install in minutes.
Website: https://substratesystems.io/endstate · Download: https://substratesystems.io/download
The GUI is designed to consume Endstate strictly through its public CLI interface, ensuring a clear separation between the engine and the user interface.
Endstate is governed by a set of public principles documented in the engine repository: Endstate Principles. These apply to both the engine and the GUI and constrain product, pricing, and architecture decisions.
Endstate GUI is in active development approaching v1.0 release.
- Node.js (v18+) and npm
- Rust via rustup (provides
cargoandrustc) - Microsoft Visual C++ Build Tools with:
- MSVC v143 (or later) C++ build tools
- Windows 10/11 SDK
- These are required for the Rust linker and native compilation
Install Build Tools via Visual Studio Build Tools or the full Visual Studio installer.
npm install
npm run tauri devIf src-tauri/icons/ is empty or icons need updating:
npm run tauri icon app-icon.pngEndstate GUI relies on the Endstate CLI being installed and available on the system PATH.
All operations are executed by invoking the CLI and consuming its structured output.
Both Endstate (the core engine) and Endstate GUI are open source, licensed under the Apache License 2.0.
All visible interactive UI components must use shadcn/ui components unless there is a documented exception.
This ensures:
- Consistent theming and dark mode support
- Accessible keyboard navigation
- Predictable behavior across the application
Use these shadcn components for interactive elements:
- Select (
@/components/ui/select) - for dropdowns and selectors - Button (
@/components/ui/button) - for all clickable actions - Checkbox (
@/components/ui/checkbox) - for boolean toggles in forms - RadioGroup (
@/components/ui/radio-group) - for mutually exclusive options - Switch (
@/components/ui/switch) - for on/off toggles - Dialog (
@/components/ui/dialog) - for modals and overlays - Input (
@/components/ui/input) - for text inputs
Native HTML elements may be used for:
- Custom interactive patterns without shadcn equivalents (e.g., collapsible sections, custom toggle groups)
- Non-interactive semantic markup
- Cases where shadcn components would conflict with existing behavior
All exceptions should be documented in code comments.
-
Thin GUI: Endstate GUI contains no business logic, no provisioning logic, and makes no assumptions about internal CLI implementation.
-
CLI as Source of Truth: All operations are executed by CLI invocation. GUI is purely a presentation layer.
-
Explicit Versioning: Both CLI and schema versions are explicit and machine-readable.
-
Graceful Degradation: Unknown fields in JSON responses are ignored by the GUI.
During development, Endstate GUI resolves the CLI from the system PATH:
- CLI Resolution:
endstatecommand resolved from PATH - Execution: Node.js
child_process.spawn - Validation: Capabilities handshake on startup
Production builds of Endstate GUI will bundle the Endstate engine as Tauri resources:
- CLI Resolution: Bundled engine scripts at known resource path
- Execution: Tauri/Rust Command API
- Validation: Capabilities handshake on startup
Note: Engine bundling is in progress. Currently requires Endstate CLI on system PATH.
GUI starts
│
├─► Call: endstate capabilities --json
│
├─► Parse response
│ │
│ ├─► Check schemaVersion
│ │ │
│ │ ├─► Compatible? → Proceed
│ │ │
│ │ └─► Incompatible? → Show error, refuse execution
│ │
│ └─► Check cliVersion (informational)
│
└─► Ready for user commands
All CLI commands with --json flag return a standardized envelope:
{
"schemaVersion": "1.0",
"cliVersion": "1.3.0",
"command": "apply",
"runId": "20241220-143052",
"timestampUtc": "2024-12-20T14:30:52Z",
"success": true,
"data": { ... },
"error": null
}See docs/ai/PROJECT_RULES.md in the endstate repository for the authoritative contract rules.
| Command | JSON Support | Description |
|---|---|---|
capabilities |
✅ | Report CLI capabilities for handshake |
apply |
✅ | Execute provisioning plan |
verify |
✅ | Verify machine state against manifest |
report |
✅ | Retrieve run history |
- Current Schema Version:
1.0 - Additive changes are backward-compatible
- Breaking changes require schema major version bump
- GUI must refuse execution if schema version is incompatible
Endstate GUI surfaces the hosted-backup capability of the bundled engine
(endstate backup *, available from engine v2.0.0). All cryptography, HTTP,
JWT validation, and keychain access live in the engine — the GUI is a thin
presentation layer per the Hosted Backup contract.
- Hosted Backup pane — listed in the sidebar when the bundled engine
advertises
features.hostedBackup.supported = true. Hidden otherwise. - Sign-in / Sign-up / Recover — three-tab auth pane. Sign-up generates a 24-word recovery key; the user must save it via at least two of three methods (file, PDF, clipboard) before continuing. There is no skip button: per the contract trust model, a user who skips the recovery key and forgets their passphrase loses their data with no recourse from us.
- Backup pane — subscription banner (
active/grace/cancelled/none), backup list, version list, push / restore / delete with streaming progress dialogs. - Restore-on-new-machine wizard — auto-prompts when a signed-in user has remote backups but zero local profiles.
- Account section in Settings — email, subscription pill, manage link, sign-out, delete-account confirmation (email-match gate).
For v1, the Subscribe / Manage links go to:
https://substratesystems.io/#pricing— Subscribe (statenone)https://substratesystems.io/account— Manage subscription (stateactive/grace/cancelled)
Both open via Tauri's shell plugin (@tauri-apps/plugin-shell).
On boot, the GUI reads endstate capabilities --json and checks
features.hostedBackup.supported. If absent or false, the auth pane,
Backup nav entry, and Account section are all hidden, and the user sees
"Update Endstate to enable Hosted Backup." Local provisioning continues to
work.
After install on a clean machine:
- Sign up with a throwaway email + 12+ char passphrase
- Save the recovery key via two of {file, PDF, copy} — confirm the dialog does not let you Continue with fewer than two saves and does not close on Escape
- Land on the backup pane; confirm the subscription banner reflects the real state from substrate
- Push a small test profile; cancel mid-push and confirm the toast
- Pull to a different location; verify byte-equal round-trip
- Sign out, sign back in on a different machine; confirm the wizard prompts to restore
- Sign out again, choose "I forgot my passphrase", paste the saved 24-word recovery key, set a new passphrase
- Confirm the new passphrase signs in and the OLD passphrase no longer works (negative test — the recovery flow rotated keys)
- From the account section, type the email exactly to enable the Confirm button; delete the account and confirm signed-out state
Free code signing provided by SignPath.io, certificate by SignPath Foundation.
Team roles:
- Committers and reviewers: Hugo Ander Kivi
- Approvers: Hugo Ander Kivi
Privacy policy: This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it.
Licensed under the Apache License, Version 2.0. See the LICENSE file for details.
Copyright 2025-2026 Substrate Systems OÜ.
The GUI includes an EngineAdapter module (src-tauri/src/engine_adapter.rs) that provides streaming NDJSON output from the Endstate CLI to the frontend.
Features:
- Spawns the
endstateCLI process with configurable arguments - Reads stdout/stderr concurrently (non-blocking)
- Parses each line: valid JSON is passed through, plain text becomes log events
- Emits events to frontend via Tauri event channel
endstate://event - Emits fallback result if CLI exits without a terminal result event
- runId tagging: Every emitted event includes a
runIdfield for tracking - One-run-at-a-time guard (v1): Only one CLI run can be active at a time
- Cancellation support: Running processes can be cancelled via
engine_cancel
Event Types:
- Log events:
{"type":"log","level":"info|warn|error","message":"...","runId":"..."} - CLI envelope: Full JSON response from CLI with
success,command,data,runIdfields - Fallback result:
{"type":"result","ok":true|false,"command":"...","summary":{"exitCode":N},"raw":null,"runId":"..."} - Cancelled result:
{"type":"result","ok":false,"command":"...","summary":{"cancelled":true,"exitCode":N},"raw":null,"runId":"..."}
- Ensure
endstateCLI is installed and on PATH - Run
npm run tauri dev - Wait for CLI status to show "ready"
- Click Capabilities to test streaming output
- For Verify and Apply, update
SAMPLE_MANIFEST_PATHinsrc/App.tsxto point to a valid manifest file - The current Run ID is displayed above the log when a run is active
- Click Cancel to terminate a running process (emits a cancelled result)
To cancel a running command:
- Click the Cancel button while a run is active
- The running CLI process will be terminated
- A cancelled result event will be emitted with
summary.cancelled: true - The UI will show the run as failed with cancellation info
Note: Only one run can be active at a time (v1 limitation). Attempting to start a new run while one is in progress will return an error.
cd src-tauri
cargo testThis runs unit tests for the parsing logic, runId injection, and cancellation in engine_adapter.rs.
This repository exists to develop the official Endstate desktop experience.