English | 日本語 | 简体中文 | Русский | 한국어 | Tiếng Việt
Windows environment variable manager built with Tauri v2, React, TypeScript, and Rust.
Envarly collects no telemetry and sends no data anywhere. The only network request the app makes is a passive daily check against the GitHub Releases API for a newer version — it sends nothing about you or your machine, just a GET request.
(The landing page uses standard web analytics to understand traffic — that's separate from the app itself, which has none.)
- 2-pane UI — sidebar variable list with search/filter and scope tabs (All / User / System), detail editor on the right
- List editor — drag-and-drop reordering for PATH, PATHEXT, NO_PROXY, and any semicolon- or comma-separated variable; auto-detects separator from name and value; supports manual override between list and plain-text modes
- Path validation — per-entry existence check (green ✓ / red ✗) for PATH-style entries; PATHEXT skips filesystem checks since its entries are extensions, not paths
%VAR%reference lint — warns on unresolvable%VAR%references in path entries; evaluated on focus-out; suppresses false positives for Windows built-in volatile vars (USERPROFILE,APPDATA,TEMP, …)- Local undo (Ctrl+Z) — multi-step undo of unsaved edits in the detail panel, before staging; drag reorder and text edits are separate undo steps; works even when a text field has focus
- Dark / light mode — follows system preference on first launch; persists across sessions; no flash on load
- Snapshot / time-travel — save named snapshots, restore to any previous state
- Diff detection — detects registry changes made by other processes while Envarly is open; shows a diff with selective apply (accept or revert per entry)
- Apply progress & log — a progress bar and per-variable log (✓/✗) show exactly what happened while staged changes are written to the registry
- Update check — checks GitHub Releases once per day; shows a subtle header badge linking to the release when a newer version is available
- Import / Export — read/write
.jsonand.regformats; export to IaC formats (PowerShell script, DSC v2/v3, Ansible playbook)- Custom export: pick individual variables, with secret-variable warnings
- Import merge strategy: Merge (additive) or Replace (sync — removes variables not in the file)
- Preview before any write; registry is never touched until you explicitly click Apply
- PATH management — stage adding Envarly's install directory to User or System PATH directly from within the app; PATH banner on launch if not yet added; button in the PATH variable detail panel
- Apply confirmation diff — Delta view (git-diff-style
+/−per entry) and Full view (all entries with change markers) for semicolon-separated values like PATH; non-list values shown in full without truncation - Critical variable warning — staging changes to
SYSTEMROOT,WINDIR, orCOMSPECshows a prominent warning in the Apply confirmation dialog - Secret detection — name-based + value-pattern detection across 35+ token formats (GitHub
ghp_, GitLabglpat-, Slackxoxb-, Anthropicsk-ant-, npmnpm_, PyPIpypi-, …); service badge shown on each match; ⚠ Secrets sidebar tab; export confirmation lists affected services - Admin elevation — "Run as admin" button restarts the process elevated via UAC; system variables become editable; "Restart as admin →" inline hint appears in the detail panel when viewing a System variable without elevation
- Edit other accounts' variables — when elevated, switch to any local account (including ones not currently logged in) to view/edit its per-user variables and PATH; the header account picker shows "My variables" until one is selected
- CLI mode — read-only subcommands (
get,list,export) plus write subcommandsimport(merge/replace from a.json/.regfile),set, anddelete(single variable) run from a terminal without launching the GUI; every write subcommand is dry-run by default and only touches the registry when passed--apply - WM_SETTINGCHANGE broadcast — running apps pick up changes without a restart
- Resizable panels — drag the sidebar and snapshots panel to the width you want; sizes persist across restarts
- Check command — find out why a command isn't found: searches the effective PATH (User + System merged, PATHEXT-aware) for a name or exe path and flags shadowed duplicates
- 6 languages — English, 日本語, 简体中文, Русский, 한국어, and Tiếng Việt; switch anytime from the header, including the ~140 built-in variable descriptions
| Layer | Tech |
|---|---|
| Desktop shell | Tauri v2 |
| Frontend | React 19 + TypeScript |
| Styling | Tailwind CSS v4 |
| Rust backend | winreg, clap, serde_json, chrono, thiserror, reqwest (native-tls) |
| Linter / formatter | Biome |
| JS tests | Vitest + @testing-library/react |
| Rust tests | cargo test (MemBackend — no real registry writes) |
| Component explorer | Storybook 10 + addon-a11y |
| Runtime versions | mise (Node 22, Rust stable) |
- mise — installs the correct Node and Rust versions automatically
- Windows 10 / 11 (registry access required)
mise install # install Node 22 and Rust stable
npm install # install JS dependencies
cd src-tauri && cargo fetch && cd .. # prefetch Rust cratesnpm run dev # start Envarly with Tauri + Vite hot reload
npm run dev:web # frontend only; Windows environment data is unavailableRun these commands from the repository root. If mise is not activated in the current PowerShell
session, use mise exec -- npm run dev.
npm test # Vitest in watch mode
npm run coverage # coverage report (V8)
npx vitest run # single run (used in CI)
cd src-tauri && cargo test # Rust unit tests (runs on Linux/macOS too)npm run storybook # http://localhost:6006 — includes axe-core a11y audit per storynpm run lint # Biome check
npm run lint:fix # auto-fix
npm run format # format only
npm run check-version # verify package.json / tauri.conf.json / Cargo.toml versions matchnpm run tauri build # produces installer in src-tauri/target/release/bundle/Release artifact policy and installer checks are documented in docs/distribution.md.
npm version patch # or minor / major
# → bumps package.json, syncs tauri.conf.json, Cargo.toml, Cargo.lock, and
# lp/src/lib/lpContent.ts, commits + tags
git push --follow-tags
# → triggers the Release workflow on GitHub Actions (builds installer, uploads to GitHub Releases)CLI subcommands work in debug builds or when launched from an existing terminal in release. In release GUI builds, stdout is not attached to the console (Windows subsystem).
# Print a variable's value
envarly get PATH
envarly get JAVA_HOME --scope user
# List all variables
envarly list
envarly list --scope system --format json
# Export to file or stdout (read-only — does not modify the registry)
envarly export --format json --output backup.json
envarly export --format reg --output backup.reg
envarly export --format ps1 --output backup.ps1 # PowerShell [Environment]::SetEnvironmentVariable
envarly export --format dsc_v2 --output backup.ps1 # PowerShell DSC v2 configuration
envarly export --format dsc_v3 --output backup.dsc.yaml # DSC v3 YAML
envarly export --format ansible --output backup.yml # Ansible environment playbook
envarly export --format json | jq '.user.PATH'
# Import from a file — dry-run by default (prints the diff, writes nothing)
envarly import backup.json
envarly import backup.reg --format reg --scope user --strategy replace
# Add --apply to actually write the changes to the registry
envarly import backup.json --apply
# Set or delete a single variable — same dry-run-by-default / --apply model.
# Unlike `setx`, this broadcasts WM_SETTINGCHANGE so running processes see the change.
envarly set JAVA_HOME C:\jdk21 --scope user --apply
envarly set MY_PATH "%JAVA_HOME%\bin" --kind expand-string --apply
envarly delete OLD_VAR --scope user --applyDemo mode opens the GUI with fixture data and keeps every change in memory, so it is safe for screenshots, walkthroughs, and release media.
envarly --demo
envarly --demo --demo-fixture path\to\fixture.json
envarly --demo-fixture=path\to\fixture.jsonThe bundled fixture lives at src/demo/envarly-demo.json. Custom fixtures use the same shape and can include User/System variables, snapshots, PATH validation results, and an initial baseline for external-change demos.
Open the Import / Export tab.
| Scope | What's included |
|---|---|
| All | Every User + System variable |
| User | User variables only |
| System | System variables only |
| Custom | Hand-pick individual variables from a checklist |
Supported formats:
| Format | Description |
|---|---|
.json |
Envarly-native |
.reg |
Windows Registry Script |
.ps1 (PowerShell) |
[Environment]::SetEnvironmentVariable calls |
.ps1 (DSC v2) |
PowerShell Desired State Configuration v2 |
.dsc.yaml (DSC v3) |
DSC v3 YAML |
.yml (Ansible) |
Ansible playbook with ansible.builtin.set_fact |
In Custom mode, variables whose names or values match known secret patterns are flagged with a ⚠ ServiceName badge.
When clicking Export, Envarly checks whether the selection contains secrets. If it does, a confirmation step lists the affected services (e.g. "AWS, GitHub credentials will be included"). Export only proceeds after explicit confirmation.
- Upload a file or paste its content, then click Parse to preview.
- Check the variables you want to apply.
- Choose a merge strategy:
- Merge — adds / updates variables from the file; leaves everything else untouched.
- Replace — makes the target scope exactly match the file (deletes variables not present in the file). A danger banner appears as a reminder.
- Click Apply Selected — this is the only point where the registry is written.
src/lib/secrets.ts provides two complementary detection strategies:
Name-based — detectSecret(name): SecretInfo | null
- Exact match against ~25 well-known variable names (
AWS_SECRET_ACCESS_KEY,GITHUB_TOKEN,DATABASE_URL, …) - Service-prefix + secret keyword (
STRIPE_SECRET_KEY→ Stripe,AZURE_CLIENT_SECRET→ Azure, …) - Generic keyword fallback (
MY_WEBHOOK_URL→ Generic)
Value-based — detectSecretByValue(value): SecretInfo | null
Recognises ~35 structured token formats by their prefix regardless of variable name:
| Prefix | Service |
|---|---|
ghp_ gho_ ghs_ github_pat_ |
GitHub |
glpat- gloas- glcbt- |
GitLab |
xoxb- xoxp- xapp- |
Slack |
sk-ant- |
Anthropic |
sk-proj- |
OpenAI |
sk_live_ sk_test_ |
Stripe |
AKIA / ASIA (20-char) |
AWS |
npm_ |
npm |
pypi- |
PyPI |
hf_ |
HuggingFace |
dapi |
Databricks |
hvs. hvb. |
HashiCorp Vault |
SG. |
SendGrid |
shpat_ shpss_ |
Shopify |
pscale_tkn_ |
PlanetScale |
Combined — resolveSecret(name, value): SecretInfo | null — name first, value fallback. Use this when both are available (variable list, import preview, export).
Detected secrets are shown with a ⚠ ServiceName badge (e.g. ⚠ AWS, ⚠ GitHub). Values are masked with •••••••• in import/export views. The ⚠ Secrets tab in the sidebar filters the variable list to secrets only.
Snapshots are stored as JSON files under %LOCALAPPDATA%\Envarly\snapshots\. Each file contains the full user and system environment at the time of the snapshot. They can be listed, restored, or deleted from within the app.
| Workflow | Trigger | Jobs |
|---|---|---|
test.yml |
Every push | Frontend (vitest) + Rust (cargo test) + version consistency check |
release.yml |
Tag push v* or manual |
Windows build → GitHub Releases |
security.yml |
Weekly (Mon 09:00 UTC) or manual | npm audit + cargo audit |
envarly/
├── src/ # React frontend — components/, hooks/, lib/, api.ts, types.ts
├── src-tauri/ # Rust backend — Tauri commands, registry access, export formats, snapshots
├── lp/ # Astro landing page (published to GitHub Pages)
├── scripts/ # Version sync/check, license generation
├── docs/ # Release/distribution notes
└── .github/workflows/ # CI: tests, release builds, security audit
Frontend code is organized by feature under src/components/, with shared hooks in src/hooks/ and framework-agnostic logic in src/lib/. The Rust backend mirrors this: src-tauri/src/commands/ holds one module per domain (env, snapshot, export, path, launch, update), with the underlying logic in env_store.rs (backend-agnostic) and env_backend.rs (the Windows registry implementation).
computeDiff()insrc/lib/diff.ts— pure function comparing twoEnvSnapshotobjects; returns structuredDiffEntry[](added / removed / changed, with scope).react-diff-viewer-continued— visual text diff for long values like PATH (split on;).
The baseline snapshot is captured on app mount. Every Refresh call re-reads the registry and compares; if the snapshots differ, a Changes tab appears automatically.
parse_import (Rust) only deserialises the file and returns a snapshot struct — it never calls write_var. Registry writes happen only when the user clicks Apply in the frontend.
env_store.rs defines the EnvBackend trait. The production implementation, WinregBackend (in env_backend.rs), is compiled only on Windows (#[cfg(windows)]). Tests use MemBackend — an in-memory Mutex<HashMap> defined alongside the trait — that never touches the registry. cargo test runs on Linux in CI without any Windows-specific dependencies.
apply_changes_with (Rust) takes a per-change progress callback; the apply_env_changes Tauri command uses it to emit an apply-progress event for each variable as it's written, while keeping the existing atomic all-or-nothing rollback behavior unchanged. The frontend subscribes via api.onApplyProgress before calling applyEnvChanges — subscribing after would race a fast apply, since events emitted before a listener attaches are simply lost.
MIT — see LICENSE.

