Set up a new Windows PC in minutes. Endstate scans your current machine for installed apps and settings, saves them to one portable file you control, then reinstalls the apps and restores your settings on a fresh Windows install. Free, open source, local-first — no account, no telemetry. Windows today via winget; the engine is cross-platform (Linux/macOS in progress).
Website: https://substratesystems.io/endstate · Download: https://substratesystems.io/download
Author: Hugo Ander Kivi
Primary Language: Go
Status: v1.0.0 — Stable
Ready-to-apply manifests for common machine types. Each one installs a curated set of mainstream apps via winget — no configuration is written, so they are safe to apply on any machine.
| Pack | What's inside | File |
|---|---|---|
| Full-stack dev | Git, VS Code, Cursor, Zed, Windows Terminal, WSL, Docker, Node.js LTS, GitHub CLI, Postman, DBeaver, lazygit, ripgrep, zoxide, Starship, 7-Zip, Claude | starter-fullstack-dev.jsonc |
| Streamer | OBS Studio, Elgato Stream Deck, Voicemeeter, Chatterino, Discord, Spotify, CapCut, Audacity, HandBrake, VLC | starter-streamer.jsonc |
| Photo & video | Adobe Creative Cloud, darktable, GIMP, Krita, Inkscape, Kdenlive, CapCut, HandBrake, Audacity, Blender, OBS Studio, VLC | starter-photo-video.jsonc |
| Gaming | Steam, Epic Games, GOG GALAXY, Discord, MSI Afterburner, Playnite, RetroArch, Sunshine, VLC, 7-Zip | starter-gaming.jsonc |
| Privacy-first | Brave, Tor Browser, KeePassXC, Signal, Mullvad VPN, WireGuard, VeraCrypt, Cryptomator, Gpg4win, simplewall, Thunderbird, 7-Zip | starter-privacy.jsonc |
| Student | Notion, Obsidian, Anki, Zotero, Zoom, Teams, LibreOffice, Acrobat Reader, Discord, Spotify, VLC, Claude | starter-student.jsonc |
To apply a pack:
# GUI: drag the .jsonc file onto the Endstate window → Preview → Apply
# CLI:
endstate apply --profile manifests/examples/starter-fullstack-dev.jsoncThe loop that works today:
- Capture and sanitize your current machine:
endstate capture --sanitize --out my-setup.jsonc - Post the file anywhere (GitHub Gist, a repo, a message).
- The recipient drags it onto the Endstate GUI (or runs
endstate apply --profile my-setup.jsonc) → previews the plan → applies.
The sanitized manifest contains only app IDs and winget references — no machine-specific paths, timestamps, or credentials.
Rebuilding a machine after a clean install is tedious, error-prone, and mentally draining. Configuration drift accumulates silently. Manual steps get forgotten. The result is machines that cannot be reliably reconstructed.
Endstate exists to eliminate this clean install tax.
A machine should be:
- Rebuildable — from a single manifest
- Auditable — with clear records of what was applied
- Deterministic — same inputs produce same outcomes
- Safe to re-run — at any time, without side effects
Endstate is designed for developers, power users, and small teams who:
- Reinstall or migrate machines regularly
- Care about reproducibility and auditability
- Want automation without sacrificing safety or control
- Declarative desired state — describe what should be true, not how to do it
- Idempotence — re-running converges to the same result without duplicating work
- Non-destructive defaults — no silent deletions, explicit opt-in for destructive operations
- Verification-first — "it ran" is not success; success means the desired state is observable
- Separation of concerns — install ≠ configure ≠ verify
Spec → Planner → Drivers → Restorers → Verifiers → Reports/State
| Stage | Responsibility |
|---|---|
| Spec | Declarative manifest describing desired state (apps, configs, preferences) |
| Planner | Resolves spec into executable steps, detects drift, computes minimal diff |
| Drivers | Install software via platform-specific package managers (winget, apt, brew) |
| Restorers | Apply configuration files, registry keys, symlinks, preferences |
| Verifiers | Confirm desired state is achieved (file exists, app responds, config matches) |
| Reports/State | Persist run history, enable drift detection, provide human-readable logs |
endstate/
├── go-engine/ # Go engine (sole CLI implementation)
│ ├── cmd/endstate/ # CLI entrypoint
│ └── internal/ # Core packages (manifest, commands, driver, restore, verifier, etc.)
├── modules/ # Config module catalog (apps.git, apps.vscodium, etc.)
├── payload/ # Staged configuration files referenced by modules
├── bundles/ # Named module groupings (JSONC)
├── manifests/ # Desired state declarations
│ ├── examples/ # Shareable example manifests
│ ├── includes/ # Reusable manifest fragments
│ └── local/ # Machine-specific captures (gitignored)
└── tests/ # Test fixtures and shared test data
# Clone the repo
git clone https://github.com/Artexis10/endstate.git
cd endstate
# Build the CLI
cd go-engine && go build -o endstate.exe ./cmd/endstate
# Or run directly without building
cd go-engine && go run ./cmd/endstate bootstrapAfter bootstrap completes, the endstate command is available globally from any directory.
# 1. Capture current machine state
endstate capture
# 2. Preview what would be applied (dry-run)
endstate apply --manifest manifests/local/my-machine.jsonc --dry-run
# 3. Apply the manifest
endstate apply --manifest manifests/local/my-machine.jsonc
# 4. Verify end state is achieved
endstate verify --manifest manifests/local/my-machine.jsonc
# 5. Check environment health
endstate doctorHanded a profile bundle (.zip) or a manifest? rebuild does the whole fresh-machine flow — install apps, restore configuration, then verify — in a single step:
# 1. Preview the rebuild (no changes)
endstate rebuild --from MyProfile.zip --dry-run
# 2. Do it (restore is on by default, so a live run needs --confirm)
endstate rebuild --from MyProfile.zip --confirmOverwritten files are backed up first and can be undone with endstate revert. Use --no-restore to install and verify without touching configuration.
| Command | Description |
|---|---|
bootstrap |
Install endstate command to user PATH for global access |
capture |
Capture current machine state into a manifest |
plan |
Generate execution plan from manifest without applying |
apply |
Execute the plan (with optional -DryRun) |
rebuild |
Rebuild a machine from a bundle/manifest in one step (install + restore + verify; live run needs --confirm) |
restore |
Restore configuration files from manifest (requires -EnableRestore) |
export-config |
Export config files from system to export folder (inverse of restore) |
validate-export |
Validate export integrity before restore |
revert |
Revert last restore operation by restoring backups |
verify |
Check current state against manifest without modifying |
doctor |
Diagnose environment issues (missing drivers, permissions, etc.) |
report |
Show history of previous runs and their outcomes |
state |
Manage endstate state (subcommands: reset, export, import) |
Humans author manifests in JSONC (JSON with comments). Plans, state, and reports are emitted as plain JSON.
Supported formats: .jsonc (preferred), .json, .yaml, .yml
Large manifests can be split into reusable modules:
// main.jsonc
{
"version": 1,
"name": "dev-workstation",
// Include other manifest files (resolved relative to this file)
"includes": [
"./includes/dev-tools.jsonc",
"./includes/media.jsonc",
"./includes/dotfiles.jsonc"
],
// Local apps are merged with included apps
"apps": [
{ "id": "custom-tool", "refs": { "windows": "Custom.Tool" } }
]
}Include rules:
- Paths are resolved relative to the including manifest
- Arrays (
apps,restore,verify) are concatenated - Scalar fields in the root manifest take precedence
- Circular includes are detected and rejected with a clear error
Endstate prioritizes safety over speed:
| Default | Behavior |
|---|---|
| Backup before overwrite | Existing files are backed up before restoration |
| Non-destructive | No deletions unless explicitly configured |
| Dry-run support | All commands support -DryRun to preview changes |
| Explicit destructive ops | Destructive operations require explicit flags |
| Atomic operations | Failed operations roll back where possible |
| Checksum verification | Restored files are verified against expected hashes |
Backup location: state/backups/<timestamp>/
End-to-end encrypted profile backups via Endstate Cloud (or any self-host
backend implementing docs/contracts/hosted-backup-contract.md). The
engine derives keys client-side via Argon2id, encrypts each chunk with
AES-256-GCM, ships the chunks to Cloudflare R2 via short-lived presigned
URLs, and persists the refresh token + the unwrapped DEK in the OS
keychain so subsequent push / pull operations don't re-prompt for
the passphrase.
Endstate cannot decrypt user data uploaded to Hosted Backup. This is a structural property: only the user's passphrase and recovery key can unlock the data. See contract §1 for the trust model.
| Variable | Default | Purpose |
|---|---|---|
ENDSTATE_OIDC_ISSUER_URL |
https://substratesystems.io |
OIDC issuer / backend URL |
ENDSTATE_OIDC_AUDIENCE |
endstate-backup |
JWT audience claim |
ENDSTATE_BACKUP_CONCURRENCY |
4 |
Upload/download worker pool size (clamped 1–16) |
# 1. Create an account. Passphrase comes from stdin (line 1).
# The 24-word BIP39 recovery mnemonic is generated client-side and
# written to --save-recovery-to before the network call. Save this
# file somewhere offline — it is the only second-factor recovery
# path if you forget your passphrase. If you lose both the
# passphrase and the recovery file, your data is unrecoverable.
echo "<your-passphrase>" | endstate backup signup \
--email you@example.com \
--save-recovery-to ~/endstate-recovery.txt
# 2. Push a profile. The first push to a fresh account auto-creates a
# backup named "default" if --backup-id is omitted.
endstate backup push --profile <path-to-profile> --name "primary"
# 3. Pull on a different machine. After `backup login`, the engine
# caches the unwrapped DEK in the OS keychain so push/pull don't
# re-prompt.
echo "<your-passphrase>" | endstate backup login --email you@example.com
endstate backup list --json
endstate backup versions --backup-id <id> --json
endstate backup pull --backup-id <id> --to ~/restored-profile
# 4. Forgotten passphrase: stdin line 1 = recovery phrase, line 2 = new passphrase.
endstate backup recover --email you@example.com
# 5. Sign out — clears refresh token AND DEK from the keychain.
endstate backup logout
# 6. Destructive operations require --confirm.
endstate backup delete --backup-id <id> --confirm
endstate backup delete-version --backup-id <id> --version-id <id> --confirm
# 7. GDPR account deletion (hard-delete: account, subscription, all data).
endstate account delete --confirm| Command | Purpose | Stdin |
|---|---|---|
backup signup --email <addr> --save-recovery-to <path> |
Create account, write recovery file | passphrase (line 1); optional mnemonic (line 2) |
backup login --email <addr> |
Sign in; cache DEK in keychain | passphrase |
backup logout |
Clear refresh token + DEK from keychain | — |
backup status [--json] |
Report session state | — |
backup push --profile <path> [--backup-id <id>] [--name <label>] |
Encrypt and upload | — |
backup pull --backup-id <id> --to <path> [--version-id <id>] [--overwrite] |
Download and restore | — |
backup list [--json] |
List backups | — |
backup versions --backup-id <id> [--json] |
List versions of one backup | — |
backup delete --backup-id <id> --confirm |
Permanently delete a backup | — |
backup delete-version --backup-id <id> --version-id <id> --confirm |
Soft-delete a version (purged after 7 days) | — |
backup recover --email <addr> |
Reset passphrase using recovery phrase | line 1: phrase; line 2: new passphrase |
account delete --confirm |
Hard-delete account + subscription + all data | — |
--events jsonl enables NDJSON event streaming on stderr for push and
pull so the GUI can render per-chunk progress.
The capabilities response (endstate capabilities --json) advertises
the configured issuer and audience under data.features.hostedBackup,
so the GUI can gate hosted-backup UI on a single handshake.
| Requirement | Version | Purpose |
|---|---|---|
| Go | 1.22+ | Build and development |
| winget | Latest | App installation (Windows) |
Endstate uses Go's standard testing package for deterministic, offline-capable testing.
# Run all tests
cd go-engine && go test ./...
# Run a specific package's tests
cd go-engine && go test ./internal/manifest/...
# Run tests with verbose output
cd go-engine && go test -v ./...Endstate v1.0.0 is the first stable release. The CLI contract (JSON schema 1.0) is locked. Capture, apply, verify, restore, and drift detection are production-ready. Winget is the primary driver with planned expansion to macOS/Linux.
A desktop GUI is available as a separate commercial product built on top of Endstate's open-source core — substratesystems.io/endstate
Endstate is licensed under the Apache License, Version 2.0.
See the LICENSE file for details.
Copyright © 2025–2026 Substrate Systems OÜ
Created by Hugo Ander Kivi at Substrate Systems OÜ.
