Documentation

regx is a single-file Windows Registry CLI for standard users. Every command below runs without elevation. Where a command physically cannot succeed without admin rights, it says so and exits with a specific code rather than failing obscurely.

Install

Once a release is available, there is nothing to install: put regx.exe anywhere you can write and run it. It creates no registry entries, no service, no scheduled task and no %APPDATA% folder.

The first binary release has not been published yet. Build from source below; do not download executables claiming to be an official release until they appear on the GitHub releases page.

powershell
# Verify it runs and see what the environment allows
> .\regx.exe --self-check

regx self-check
  [ok  ] process          64-bit process. HKLM\SOFTWARE resolves to the 64-bit view.
  [ok  ] elevation        not elevated (medium integrity), as intended
  [ok  ] hkcu             HKCU\Software is writable - the redirection target is usable

A file downloaded with a browser carries a Mark-of-the-Web stream that triggers SmartScreen. --self-check reports it; clear it with Unblock-File .\regx.exe.

Build from source

powershell
> cargo build --release   # -> target\release\regx.exe

Requires a Rust MSVC toolchain. The build embeds the asInvoker manifest into the PE and statically links the CRT, so the result needs no Visual C++ redistributable.

Release maintainers run both check_release_identity.py --self-test and check_release_assets.py --self-test. Before publication, the first requires one exact tag/Cargo/date/changelog-notes identity and emits the notes GitHub consumes; the second checks exact asset coverage, checksums, PE architecture, elevation manifest, static CRT, CycloneDX identity/version, and the strict binary-size contract.

Generate Unix manual pages

Maintainers packaging regx for WSL or a Unix documentation system can generate section-1 pages for the root and every nested command from the same Clap metadata. The renderer is not linked into regx.exe.

shell
$ cargo run --example generate-man -- target/man

Benchmark large inputs

The development harness creates synthetic .reg, Registry.pol, and private application-hive workloads under target/benchmark-large. It measures the release executable end to end and reports throughput, operations per second, and peak working set without touching the user's registry.

shell
$ cargo run --release --example benchmark-large -- target/release/regx.exe 5000

Fuzz parser inputs

Three development-only libFuzzer targets exercise raw .reg bytes, XML, and every forced input dialect. Checked-in seeds and a deterministic 10,000-case mutation smoke are tested normally; the GitHub workflow is configured for short AddressSanitizer campaigns on parser changes and each week. Setup and crash-triage instructions live in fuzz/README.md.

Quick start

powershell
# 1. Lint and repair a .reg file you found online
regx validate app.reg --fix --backup

# 2. See where each key would land, without writing anything
regx convert app.reg --redirect auto

# 3. Check you can actually write to the destination
regx probe "HKCU\Software\Acme"

# 4. Apply it. An undo file is written before anything changes.
regx import app.reg

# 5. Changed your mind
regx undo app.undo.reg

Stream data through stdin or Windows Named Pipes

Commands that read registry data accept - once for standard input and pipe:NAME for one-shot Windows IPC. Both avoid temporary files while preserving content-based format detection.

powershell
> Get-Content app.reg -Raw | regx inspect -
> Get-Content policy.json -Raw | regx convert - --from json --redirect off

A Named Pipe producer must use byte mode, write one complete registry-data document, and close the pipe. regx waits up to five seconds and accepts at most 64 MiB. The native \\.\pipe\NAME spelling is also supported.

powershell
> $producer = Start-Job {
    $p = [IO.Pipes.NamedPipeServerStream]::new("regx-input",
      [IO.Pipes.PipeDirection]::Out, 1, [IO.Pipes.PipeTransmissionMode]::Byte)
    try {
      $p.WaitForConnection()
      $bytes = [Text.Encoding]::UTF8.GetBytes((Get-Content policy.json -Raw))
      $p.Write($bytes, 0, $bytes.Length); $p.Flush()
    } finally { $p.Dispose() }
  }
> regx inspect pipe:regx-input --from json --output json
> Wait-Job $producer | Receive-Job

Stream import and sync require -y unless they are dry runs. validate --fix requires --out, and saved plans reject streams because a closed source cannot be re-verified.

Shell completion

Generate completion from the executable's own Clap command metadata. The output therefore includes every shipped command and flag without a separately maintained script.

powershell
> regx completions powershell | Out-String | Invoke-Expression
# Also supported: bash, elvish, fish, zsh

The command writes only to standard output. Redirect it to the shell-specific completion directory or add the invocation to your profile when you want it loaded permanently.

Global flags

These apply to every command.

FlagEffect
--dry-runPerform every read — so permission problems still surface — but skip all writes.
-y, --yesSkip confirmation prompts.
--output text|jsonMachine-readable output for pipelines.
--view 64|32|bothChoose the WOW64 registry view explicitly. Read-only live commands report both views separately. Mutations, batch operations, backup/restore, copy/move, saved plans, and pruning sync keep independent per-view state, artifacts, undo and cross-view rollback. Commands that do not access a registry view simply have no view-specific effect.
--log-levelerror, warn, info (default) or debug.
--self-checkReport what AppLocker, SRP, WDAC and the process token do to this binary, then continue or exit.

Exit codes

Stable across releases, so a script can branch on them.

CodeMeaningTypical cause
0Success
2Usage errorUnknown flag, missing argument.
3Parse errorThe .reg file has invalid syntax.
4Access deniedThe key exists but this token cannot write to it.
5Partial successSome keys were skipped or some subkeys were unreadable.
6Redirection refusedA key had no per-user equivalent and --on-refuse fail was set.
7File I/O errorCould not read or write a file on disk.
8Not foundThe key or value does not exist.

validate

Parse and lint one or more .reg files without touching the registry. Use inspect for structural and fidelity validation of any supported input format.

usage
regx validate <FILE...> [--strict] [--fix [-o FILE] [--backup]]

What --fix repairs

Safe repairs are unambiguously what the author meant. Lossy repairs change bytes — they are still applied, because the file is already broken, but they are labelled so you can judge.

DefectRepairClass
hex(1)/hex(2)/hex(6) missing NUL terminatorAppend 00,00safe
hex(7) missing double NULAppend until the list terminatessafe
Trailing whitespace after a \ continuationRemoved — regedit stops folding there and drops the rest of the payloadsafe
Control characters in a key path or value nameStrippedsafe
Duplicate key blocksCoalesced, last write winssafe / lossy
Odd-length UTF-16 payloadPadded with one NUL bytelossy
hex(4)/hex(b) of the wrong lengthReported, never guessednot fixed

--fix refuses syntax errors and accepts exactly one input per invocation. This prevents a later invalid file from leaving an earlier file repaired as a partial multi-file operation. With --output json, a requested repair includes repairedData: the complete lossless registry-data object that would be or was written. It is available during --dry-run, preserves numeric type IDs and raw bytes, and is null for read-only validation or syntax-error refusal. A written repair additionally reports its exact output path, byte length and SHA-256. In-place --backup reports the same evidence independently and no longer contaminates JSON stdout with a text-only status line.

convert

Transform a .reg file offline. This command never touches the registry, so it is the safe way to preview a redirection.

usage
regx convert <FILE> [-o FILE] [--to reg|json|csv|pol] [--redirect MODE] [--conflicts last-wins|error] [--min-confidence LEVEL] [--reg4]

With no -o, the result is written to standard output. --conflicts error rejects semantic drift inside the source or introduced by Smart Redirection before writing either destination. --reg4 emits the legacy ANSI REGEDIT4 dialect instead of UTF-16 Version 5.00. File and stdout bytes use the machine's active ANSI codepage and refuses best-fit substitution; use V5 when text is not representable.

--to json emits the explicit {"keys": [...]} schema and --to csv emits spreadsheet-ready rows. Both preserve unknown registry types and malformed payloads as a numeric type id plus raw hexadecimal bytes, so converting back to .reg is byte-exact rather than based on display text.

--to pol writes a version-1 binary Registry.pol for one implicit HKCU or HKLM policy root. Empty keys, strings, DWORDs, MS-GPREG-defined raw types, named-value deletion and subtree deletion round-trip exactly. Mixed hives, implicit-root/default-value mutation, undefined types and record payloads above 65,535 bytes are refused before output because the protocol does not define them.

Status and query output selected with --output json has a versioned command-to-schema catalog. Its command map identifies the correct definition for each machine-readable operation. watch validates one event per line. Data/script producers reject the ambiguous global flag: use convert --to json; merge and completions retain their native .reg and shell-source streams.

merge

Combine any supported registry-data formats into one .reg, JSON, CSV, or binary Registry.pol output. Each input is detected independently; policy-reader options such as --pol-root, --inf-language, and --admx-state are available. Semantic losses fail closed before output. Duplicate keys are folded case-insensitively with last-write-wins, and every value or key-state conflict is reported. For unattended pipelines, --conflicts error refuses the merge before creating output if inputs assign different data to the same value or disagree on whether a key is created or deleted.

usage
regx merge <FILE> <FILE...> [--to reg|json|csv|pol] [--conflicts last-wins|error] [-o FILE] [--reg4]

import, undo and sync

Merge registry-data files into the live registry. Both capture a complete undo snapshot before making any change.

usage
regx import <FILE...> [--redirect MODE] [--conflicts last-wins|error] [--value GLOB] [--exclude-value GLOB] [--backup FILE | --no-backup]
regx undo   <FILE>    [--backup REDO_FILE]
regx sync   <FILE>    [--redirect MODE] [--conflicts last-wins|error] [--prune [--prune-keys]] [--backup FILE | --no-backup]

Use --conflicts error for fail-closed automation. It rejects conflicting value data and key create/delete state found either inside one input or after combining and redirecting inputs. Rejection occurs before registry reads, undo files, audit records, or confirmation.

Repeat --value GLOB and --exclude-value GLOB to import selected value names; matching is case-insensitive and @ denotes the default value. Once selection is active, empty-key creates and whole-key deletes are omitted, so value scope cannot silently become key scope.

The undo snapshot

The registry offers no transaction, so a failed merge would otherwise leave half-applied state. Before writing, regx computes the inverse of the pending change and saves it as an ordinary .reg file beside the input. The default filename begins with the input stem and includes PID, nanosecond time, and an atomic sequence, so concurrent operations do not overwrite one another. Use --backup FILE when a script needs a stable explicit path.

  • [-KEY] that exists → the whole subtree is exported, so undo recreates it
  • Key exists, value exists → the current data is recorded
  • Key exists, value absent → "name"=- is recorded
  • Key does not exist → [-TOPMOST_MISSING_ANCESTOR], not [-KEY] — deleting only the leaf would leave the intermediate keys behind as empty shells

Restores are ordered before removals. If any key cannot be read, the operation is refused rather than trusting an incomplete inverse. If a later mutation fails after earlier writes succeeded, regx automatically applies the snapshot and reports both phases. --no-backup explicitly disables the undo file and automatic rollback. JSON results identify each per-view undo artifact and include its exact undoBytes and undoSha256. Dry-run and --no-backup use null evidence.

Apply a saved inverse with regx undo FILE. Redirection is always disabled so the recorded paths are restored exactly, and regx captures a fresh redo snapshot before changing anything. For a dual-view bundle, pass its base or either generated member together with --view both; both .32.reg and .64.reg members must exist and are restored as one cross-view atomic operation. JSON seals each persisted redo with exact redoBytes and redoSha256; dry-run uses null evidence.

sync --prune

Makes the apply idempotent: any live value under a declared key that the file does not mention becomes an explicit delete. Use it when the .reg file is meant to be the complete desired state.

Add --prune-keys to treat declared paths as the complete desired tree and recursively remove topmost branches that are not represented. This stronger mode requires --prune, reads every affected subtree before writing, refuses ACL gaps, and includes every generated delete in policy checks, the undo snapshot, audit events, and automatic rollback. Use the same flags with plan to preview the exact deletions.

export

Write a live key directly to .reg, JSON, CSV, or Registry.pol. REG output remains byte-compatible with regedit's own output so diffs are meaningful.

usage
regx export <KEY> [-o FILE] [--to reg|json|csv|pol] [--root-as KEY] [--no-recursive] [--include GLOB] [--exclude GLOB] [--value GLOB] [--exclude-value GLOB] [--reg4]

A denied subkey never aborts the export. Partial export of your own hive is normal — Group-Policy-locked policy keys and Protected subtrees are common — so skips are listed and the command exits with code 5.

Key-path filters use repeatable --include/--exclude globs, while value-name filters use the same --value/--exclude-value rules as import. Patterns match portable paths after --root-as; * stays within one path component and ** crosses separators. Export is recursive by default; --no-recursive keeps only the requested key. No match exits 8 and does not create the requested output file. Status JSON records the effective scope, format and filters, reports counts after filtering, and seals every created artifact with its exact bytes and lowercase sha256. Dry-run and inline-data views report null evidence because no file exists. --root-as KEY replaces the requested source key with a validated destination and preserves relative descendants in every output format, enabling explicit HKLM/HKU-to-HKCU migration artifacts without mutating the source.

--view both -o NAME.EXT keeps WOW64 views distinct as NAME.32.EXT and NAME.64.EXT. Without -o, add --output json to receive both datasets and per-view failures in one document; plain-text stdout is refused because concatenating two registry files would not be safely re-importable.

query, ls, stats, fingerprint, set, delete

usage
regx query  <KEY> [-v NAME] [-r]
regx ls     <KEY> [-r] [--include GLOB] [--exclude GLOB] [--limit N] [--computer COMPUTER]
regx stats  <SOURCE> [--computer COMPUTER] [--root-as KEY] [--view native|32|64|both] [--include GLOB] [--exclude GLOB] [--value GLOB] [--exclude-value GLOB]
regx fingerprint <SOURCE> [--computer COMPUTER] [--root-as KEY] [--view native|32|64|both] [--expect SHA256] [--include GLOB] [--value GLOB]
regx set    <KEY> -v NAME -t TYPE -d DATA [--redirect MODE] [--backup FILE]
regx delete <KEY> [-v NAME] [-r] [--backup FILE]

ls lists immediate child keys without opening or printing their values; -r walks all descendants. It supports independent WOW64 views and read-only remote HKLM/HKU access. Repeatable --include/--exclude globs scope canonical paths, while --limit (default 1000) bounds matches per view and reports truncated. Skipped ACL paths and per-view failures are preserved in strict JSON.

fingerprint computes canonical SHA-256 v1 over exact paths, names, deletion state, numeric types, and raw bytes without printing value data. Reordering equivalent source records leaves the digest unchanged; any exact registry-state change changes it. Files, stdin, live/remote keys, and independent WOW64 views are supported. --expect SHA256 exits 5 on drift for files and one selected view. A dual-view gate requires both --expect-32 and --expect-64, preventing a partially checked pair. Repeatable key --include/--exclude and value --value/--exclude-value globs restrict the exact state covered. JSON echoes the scope and selected counts; no match exits 8 with matched:false, never a false successful empty hash. For migrations, --root-as KEY rebases a live/remote subtree before hashing; offline hive fingerprint rebases the mounted hive root and produces the same digest as an equivalently rebased export. File inputs reject ambiguous rebasing.

stats summarizes any supported file, stdin, or live/remote key without printing value names or payloads. It reports effective key and value counts, registry types, raw payload bytes, delete operations, maximum depth, conflicts, and completeness; dual-view results remain separate. Repeatable key and value include/exclude globs scope the metrics exactly like fingerprint/export. JSON echoes that scope and matched; no match exits 8. For migration reports, --root-as KEY rebases a live/remote subtree before filtering and keeps maximum depth relative to the mapped requested root. Offline hive stats maps the mounted hive root with the same semantics. JSON records the canonical rootAs; file inputs reject ambiguous rebasing.

delete without -v removes the key and its subkeys, so it requires -r as an explicit acknowledgement.

Both mutations capture a complete inverse for every selected registry view before asking for confirmation. After acceptance, the inverse is written to a temporary .reg file; --backup FILE chooses a durable path, and --view both produces FILE.32.reg and FILE.64.reg. Cancelling writes neither registry state nor the requested backup. Temporary names include the process, nanosecond time, and an atomic sequence so concurrent commands cannot reuse one undo path. The JSON result includes each view's exact undo path.

Add --computer COMPUTER to query for read-only remote HKLM/HKU access. The same option is available on export and live-key search. It uses Windows RegConnectRegistryW; the Remote Registry service, firewall path, and remote ACLs must already permit the connection. Mutation commands intentionally have no remote option.

With --output json, every query value retains the human-readable type/data preview and adds an exact object. That object preserves typed strings and DWORDs, or the numeric registry typeId plus raw bytes for every other type. Live, remote, dual-view, and offline-hive queries therefore share one lossless automation contract.

copy & move

usage
regx copy <SOURCE> <DEST> [--overwrite] [--backup FILE]
regx move <SOURCE> <DEST> [--overwrite] [--backup FILE]
regx copy-value <SOURCE_KEY> <VALUE> <DEST_KEY> [--dest-value NAME] [--save-plan FILE]
regx move-value <SOURCE_KEY> <VALUE> <DEST_KEY> [--dest-value NAME] [--save-plan FILE]
regx copy <SOURCE> <DEST> --save-plan preview.json
regx apply-copy-plan preview.json -y

Both commands copy the complete readable subtree and write one undo snapshot before changing anything. A move is two-phase: the source is deleted only after every destination write succeeds. Existing destinations are refused unless --overwrite is supplied; overwrite merges and leaves unrelated destination values intact. An unreadable source subkey or incomplete rollback snapshot aborts the operation instead of risking silent data loss. A failure in either phase automatically applies the combined snapshot. With --view both, both source and destination pairs are preflighted and snapshotted before mutation, separate .32.reg/.64.reg undo files are written, and a later-view failure rolls back every earlier touched view. --save-plan writes a digest-bound collision preview instead of mutating. apply-copy-plan refuses source or destination drift and emits a versioned result. In dual-view mode, the preview is stored as a .32.json/.64.json pair; apply verifies both artifacts and both live snapshots before writing either view. JSON creation results seal every persisted plan independently with planBytes and planSha256, including value plans and both members of a dual-view pair. Direct mutation and apply-copy-plan results seal every persisted undo with backupBytes and backupSha256; dry-run uses null evidence. For remote-to-local migration, add --source-computer COMPUTER to copy. The source computer and subtree are bound into saved previews, while every write remains local. move intentionally exposes no remote-source option.

copy-value and move-value operate on one value while preserving every sibling value and subkey. Use @ for the unnamed default value and --dest-value to rename it. They use the same two-phase copy/delete, combined undo snapshot, audit, dry-run, JSON, remote-copy, and cross-view rollback guarantees as subtree operations. Their --save-plan artifacts bind the exact value payload and both names; version 1 subtree plans remain readable.

backup & restore

usage
regx backup  <KEY> <HIVEFILE> [--computer HOST]
regx restore <HIVEFILE> <DEST> [--overwrite] [--backup UNDO.reg]

backup creates a genuine native regf application hive without elevation. It preserves keys, empty keys, registry types, and raw value bytes. The file can be inspected or edited with the existing hive commands. --view both preflights both WOW64 views before writing NAME.32.hiv and NAME.64.hiv; if either write fails, neither artifact is kept. --computer HOST reads remote HKLM/HKU while writing only the local hive artifact; no remote mutation API is used. Successful JSON output records the exact bytes and lowercase sha256 of every created hive. Both are explicitly null during dry-run because no artifact exists yet.

restore rebases the saved root under an explicit live destination. It refuses an existing destination unless --overwrite is supplied, and routes the mutation through administrative policy, audit, a complete undo snapshot, and automatic rollback. In dual-view mode it reads the .32.hiv/.64.hiv pair, writes separate undo files, and rolls back every touched view if either restore fails. JSON seals every persisted undo snapshot with undoBytes and undoSha256, independently per view; dry-run uses null evidence because no undo file is written.

Application hives do not preserve per-key ACLs, key classes, or last-write timestamps. Windows' full-fidelity RegSaveKeyEx requires SeBackupPrivilege, so claiming those properties would conflict with regx's standard-user contract.

probe

Answers the only question that matters before an import: can this user actually write here? It really opens the key rather than inferring from the path, because an ACL on a single subkey can deny a standard user even inside their own HKCU.

powershell
> regx probe "HKLM\SOFTWARE\Microsoft"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft
  exists    true
  readable  true
  writable  false
  creatable false

probe has no side effects: when the key does not exist it walks up to the nearest existing ancestor and tests write access there instead of creating a scratch key. --computer HOST performs the same capability check against remote HKLM/HKU; it only opens handles with specific access masks and never creates or changes remote state.

permissions

Inspect the key's real security descriptor without changing it. The result includes owner SID, DACL inheritance/protection, portable SDDL, and the current token's effective access for each individual registry right.

powershell
> regx permissions "HKCU\Software\Acme"
> regx permissions "HKLM\Software\Acme" --view both --output json
> regx permissions "HKCU\Software\Acme" --compare "HKLM\Software\Acme" --exit-code

Effective query, enumerate, notify, set-value, create-subkey, and delete rights are measured by opening the key with each specific access mask—not inferred from whether the path begins with HKCU or HKLM. --view both reports the 32-bit and 64-bit views separately. --compare OTHER lists owner, inheritance, SDDL and effective-right differences; --exit-code returns 5 when drift exists. Use --computer HOST for the first key and --compare-computer HOST for the comparison key, independently, to audit local-to-remote or remote-to-remote ACL drift without enabling remote writes.

hive — offline hive files

RegLoadKey, which reg load and regedit's Load Hive both call, requires SeRestorePrivilege. RegLoadAppKeyW does not: it mounts the hive into a private, unnamed slot that only the calling process can see, so there is no global namespace entry to protect and no privilege to check.

Why mount and unmount are not separate commands

The handle is process-scoped. Closing it — including at process exit — unloads the hive. So this cannot work:

does not work
regx hive mount NTUSER.DAT --as my_hive   # process 1 exits -> hive unloaded
regx hive set my_hive\Software\App ...    # process 2: nothing is mounted
regx hive unmount my_hive                 # process 3: nothing to unmount

There is no supported workaround. The handle cannot be published to the registry namespace — that is exactly what the privilege check guards — and inheriting it into another process would require the mounting process to stay alive, i.e. a resident daemon, which defeats "portable, no install". Mount, operate and unmount therefore happen inside one process.

powershell
# Several operations under a single mount
regx hive "C:\path\MyApp.hive" --create exec \
  -c "set Software\MyApp -v License -d OK" \
  -c "set Software\MyApp -v Seats -t REG_DWORD -d 25" \
  -c "query Software\MyApp -r"

# A single operation needs no exec
regx hive "C:\path\MyApp.hive" export Software --to json -o offline.json --root-as "HKEY_USERS\OFFLINE"
regx hive "C:\path\MyApp.hive" probe Software\MyApp
regx hive "C:\path\MyApp.hive" permissions Software\MyApp --output json
regx hive "C:\path\MyApp.hive" -y batch changes.json --strip-root HKCU --backup hive-undo.reg
regx hive "C:\path\MyApp.hive" search Software license --field name
regx hive "C:\path\MyApp.hive" diff Software\MyApp desired.reg --strip-root HKCU --exit-code --to json -o drift.json
regx hive "C:\path\MyApp.hive" -y sync desired.reg --strip-root HKCU --conflicts error --prune --prune-keys --backup sync.undo.reg
regx hive "C:\path\MyApp.hive" -y copy Software\MyApp Software\MyApp.Backup --backup copy.undo.reg
regx hive "C:\path\MyApp.hive" -y move Software\MyApp.Backup Software\MyApp.Renamed
regx hive "C:\path\MyApp.hive" -y undo copy.undo.reg --backup copy.redo.reg

Operations

OperationDescription
infoSize, regf signature, and whether the hive can be mounted read-only or read/write. Run this first.
lsList subkeys; add -r for descendants. Repeatable --include/--exclude globs scope relative hive paths, and --limit bounds matching output with explicit truncation status.
statsSummarize keys, values, types, payload bytes, deletes, depth, conflicts, and completeness below a subkey without printing value payloads; key/value globs scope managed state, and --root-as normalizes migration roots before filtering while preserving relative depth.
fingerprintCompute the canonical SHA-256 v1 for exact subtree state without printing value names or payloads; --expect exits 5 on drift, key/value globs scope state, and --root-as normalizes migration roots.
queryPrint values under a subkey.
probeTest whether a subkey exists and can be read, changed, or created without modifying the hive.
permissionsReport owner SID, inheritance, SDDL, and effective query/enumerate/notify/set/create/delete access for a subkey.
searchSearch keys, value names, types, or data below a subkey using bounded substring, glob, or Unicode regex matching, with path/value include/exclude filters and result limits.
diffCompare a subtree directly with any supported registry-data file, optionally write the applicable drift patch, scope key paths or value names, summarize, or return exit 5 when different. Value selection omits structural key changes so unselected siblings cannot be deleted.
setWrite one value, with a persisted inverse selectable through --backup.
deleteDelete a subkey or a single value, with a persisted inverse selectable through --backup.
copy / moveCopy, move, or rename a complete subtree. Existing destinations require --overwrite; recursive self-destinations are rejected, partial operations roll back, and --backup persists the inverse.
copy-value / move-valueCopy, move, or rename one value without copying sibling values or subkeys. --backup persists the inverse.
importMerge any supported registry-data file in. Content-first detection and the shared POL/INF/ADMX selectors apply; fidelity losses fail before snapshotting. --strip-root removes the mount-point prefix; --conflicts error rejects duplicate semantic drift before snapshot or mutation; --backup selects the undo artifact.
undoRestore a generated inverse with its HKCU mount label removed automatically, while persisting a fresh redo snapshot before any hive change.
syncReconcile any supported registry-data file into the hive through the shared lossless reader pipeline. --conflicts error fails closed after root stripping; --prune removes undeclared values, --prune-keys removes unrepresented subtrees, and --backup selects the complete inverse.
batchApply the published v1 JSON manifest atomically under one mount. Every operation is re-rooted and policy-checked before any write, one shared inverse is persisted, and a mid-batch failure rolls back all earlier operations.
exportWrite part of the hive as REG, JSON, CSV, or Registry.pol. --root-as assigns every exported path a real registry key because an application hive has no permanent namespace; --no-recursive, key-path --include/--exclude, and value-name --value/--exclude-value globs scope the artifact. Filters match the rebased portable paths. Empty selections create no file, and states Registry.pol cannot encode are refused before output.
execRun several operations under one mount, via repeated -c or a --script file.

What you can realistically open

A private application-hive handle has one namespace, not separate WOW64 views. Hive operations therefore reject --view 32, --view 64, and --view both instead of silently ignoring them.

Every mutation prompts unless -y is allowed. Administrative RequireConfirm disables that bypass for set, delete, import, sync, subtree and value copy/move alike. Every single-operation mutation takes a complete inverse before the prompt, persists it only after acceptance, and automatically applies it after a partial failure. Use --backup FILE to choose the path, then reapply it with hive HIVEFILE undo FILE -y. Undo automatically removes the snapshot's HKCU mount label and captures another reversible redo snapshot. Ordinary mutations expose undo in JSON; this command exposes the new artifact unambiguously as redo, with exact byte length and SHA-256 evidence for either artifact. Dry-run uses null evidence. Both forms also include the apply and rollback reports. Default temporary undo names use the same collision-resistant allocator; repeated mutations inside one hive exec receive distinct paths. Root deletion is rejected during preflight, including when it is hidden inside an import, sync, or batch manifest.

Write access to the file is still required, and a hive already mounted by the OS is held exclusively. Realistic targets: a logged-off secondary profile, a copy of a hive, an application's private hive, or a hive on a mounted backup or VHD. A logged-on user's NTUSER.DAT fails with ERROR_SHARING_VIOLATION by design — hive info reports this before you commit to a write.

inspect

Report what a file is and what it contains, without applying any of it. Use it on anything you did not write yourself before deciding whether to import.

powershell
> regx inspect "C:\Windows\System32\GroupPolicy\Machine\Registry.pol"

  format      pol
  key blocks  2 (0 whole-key delete(s))
  values      6
  hives       HKEY_LOCAL_MACHINE
  note        5 policy record(s)
  note        policy paths rooted at HKEY_LOCAL_MACHINE (a .pol stores no hive)
  1 key(s) have no per-user equivalent; `regx convert` shows which

Text and JSON output include the detected source encoding; registry files also report REGEDIT4 versus Windows Registry Editor Version 5.00. Duplicate semantic drift exits 5. Text identifies every conflicting path/value and its before/after preview; JSON exposes the strict conflicts[] objects with source line numbers plus oldExact/newExact typed/raw value payloads so automation can repair the input before selecting --conflicts error. Whole-key conflicts use null exact payloads because no individual value exists. Each JSON report additionally embeds data, the complete lossless parsed registry-data model. It remains inspectable for an incomplete or conflicting source even when convert correctly refuses to emit that partial model as a safe artifact. regx formats lists every supported format and how each is detected.

discover

Enterprise executables find their own configuration by anchoring on GetModuleFileNameW(NULL) — the real path of the running module. It is used rather than argv[0] because a parent process chooses argv[0] and can point it anywhere. Strip the extension, append .ini, and that is the classic sidecar; .NET reaches MyApp.exe.config the same way. Around that, products layer a search order.

discover reproduces that search, reports which rung each hit came from, and flags the rungs that are load-bearing security bugs.

powershell
> regx discover "C:\Program Files\Acme\updater.exe" --strict

anchor      C:\Program Files\Acme
stem        updater

4 companion file(s), in search order:

  [3] beside the executable  C:\Program Files\Acme\updater.ini
      ini        84 bytes
  [9] current directory      D:\Shared\updater.ini
      ini        74 bytes
      RISK   CurrentDirectory: anyone who can write there controls this configuration

The search order

RankOriginNotes
1explicit pathPassing a config file rather than an executable anchors on its directory and records the file here.
2environment variable<STEM>_CONFIG, <STEM>_HOME, <STEM>_INI.
3beside the executableThe sidecar, plus the .exe.<ext> convention.
4–6%LOCALAPPDATA%, %APPDATA%, %PROGRAMDATA%Under a \<stem>\ subdirectory.
7registry pointerSoftware\<stem> ConfigPath. Opt in with --registry-pointer.
8Group Policy cachesRegistry.pol and PolicyDefinitions. Opt in with --policy.
9current directoryAlways probed so its risk can be reported. Never trusted.
10%WINDIR%Where GetPrivateProfileString silently resolves a bare file name.

Risks reported

RiskWhy it matters
CurrentDirectoryAnyone who can write to a directory a user launches from controls that configuration. This is config planting — the same shape as DLL planting.
UserWritableThe file sits somewhere this user can write, while the executable's own directory is protected. A lower-privileged location overriding a higher-privileged one.
ReparsePointReached through a symlink or junction; the real target may be elsewhere entirely.
EscapesAnchorA sidecar whose resolved path leaves the anchor directory got there through a link.
NetworkPathUNC or a mapped drive: availability and integrity are not local.
WindowsFallbackThe %WINDIR% resolution of the profile-string APIs, which plenty of legacy code hits without intending to.
ShortNameAliasMatched only after 8.3 expansion, so path comparisons elsewhere in the system may not agree that these are the same file.

Directory writability is asked of the OS, not inferred from the path: the directory is opened for FILE_ADD_FILE, which is an access check with no side effect — the same principle as probe. And discover reports what an application would find; it does not claim to know which rungs a given product actually implements. Confirm before trusting a hit.

With --output json, the versioned report includes the resolved executable and anchor, enabled discovery controls, notes, every probed-but-absent candidate in searched, and the aggregate risky count. Every hit keeps both its candidate path and canonical resolvedPath, alongside stable risk names and structured riskDetails explanations. JSON always retains that full probe trail; --verbose only expands text output. --strict exits 5 if any hit carries a risk, so a deployment check can gate on it.

diff

Compare any two sources and emit the patch between them. Each side is either a file in any supported format or a live registry key, so file-to-file, file-to-live and live-to-live all work from the same argument positions.

powershell
> regx diff baseline.reg "HKCU\Software\Acme" --exit-code

~ HKEY_CURRENT_USER\Software\Acme\Channel
    - stable
    + beta
+ HKEY_CURRENT_USER\Software\Acme\NewFlag = 0x00000001 (1)
- HKEY_CURRENT_USER\Software\Acme\Retries = 0x00000003 (3)

1 added, 1 modified, 1 removed

The patch written by -o turns A into B. So a drift report is also the fix, and swapping the arguments produces the rollback. --exit-code exits 5 when the two sides differ, which makes it usable as a deployment gate. If either input contains parse losses or conflicting duplicate assignments, the visible comparison is still reported as incomplete and exits 5, but -o refuses to write a potentially unsafe patch. Select --to reg|json|csv|pol to write that patch directly in any round-trip registry-data format; an unrepresentable Registry.pol patch fails closed. JSON status seals each created patch with exact bytes and lowercase sha256. Dry-run, omitted and incomplete-source patches carry explicit null evidence.

--map-a FROM=TO and --map-b FROM=TO rebase a complete input subtree before comparison. Use this for migrations where equivalent settings live under different roots, such as HKLM to HKCU. Every key on that side must be beneath FROM; invalid or partial mappings are rejected. Counts, filters, JSON changes, and the generated A-to-B patch all use the mapped destination path.

Repeat --value GLOB and --exclude-value GLOB to scope comparison to value names; use @ for the default value. Activating value selection removes structural key changes from the diff. If the target lacks an entire key, the patch therefore emits only selected value deletions and cannot remove unselected sibling values.

JSON value changes retain the compatible left/right previews and add leftExact/rightExact. A present side carries its typed value or numeric type ID and raw bytes; an absent side is null. The same lossless change contract is used by single-view, dual-view, remote, and offline-hive diff.

Repeat case-insensitive glob --include/--exclude options to scope a large tree. --summary-only emits counts without individual changes. The same filtered diff drives counts, exit status, JSON, and -o, so a summary-only patch remains complete for the selected scope.

If either side is live, --view both compares the file/live or live/live pair independently in the 32-bit and 64-bit views. -o PATCH.json --to json writes PATCH.32.json and PATCH.64.json; JSON retains per-view counts, completeness, changes, patch path, write status and failures. The paired output is fail-closed: neither patch is written if either view fails or is incomplete. A stdin side is read once and reused for both views.

--computer-a HOST and --computer-b HOST independently read a live HKLM/HKU side through Windows Remote Registry. This supports remote-to-file, remote-to-local, and remote-to-remote drift checks without enabling remote mutation; either option is rejected when its corresponding side is a file, stdin, HKCU, or another unsupported root.

Comparison is case-insensitive on key paths and value names, because the registry is, but byte-exact on data: a REG_SZ and a REG_EXPAND_SZ holding the same characters are a difference, not a match, since one is expanded by the consuming application and the other is not.

watch

Wait for native Windows registry notifications without polling, then compare snapshots to identify the keys and values that changed. JSON is emitted one object per line for streaming automation. Every value change includes lossless leftExact/rightExact objects; added or removed sides use null, while present sides preserve typed values or numeric type ID and raw bytes.

powershell
> regx watch "HKCU\Software\Acme" --count 10 --timeout 300 --output json
> regx watch "HKCU\Environment" --no-recursive

A zero timeout waits indefinitely. A bounded timeout with no change returns success and emits timedOut: true. Recursive watch is the default; unreadable descendants make the baseline or result incomplete and exit 5. --view both arms native Win32 events for the 32-bit and 64-bit keys and waits on both without polling. Each event identifies triggeredView and includes separate snapshot diffs for both views. Carrying the before/after payload in the notification avoids a race-prone follow-up query.

plan

Resolve an import or sync before mutation. Unlike a summary-only dry run, the plan lists every key/value operation with before and after state, Smart Redirection outcomes, administrative-policy decisions and rollback completeness. By default it writes nothing; --save FILE explicitly writes only a digest-bound plan artifact, never registry state, audit records, or undo files.

powershell
> regx plan app.reg --output json
> regx plan desired.json --conflicts error --prune --redirect auto --save rollout.plan.json
> regx apply-plan rollout.plan.json -y

One denied destination blocks the whole plan, matching import's all-or-nothing policy boundary. Redacted audit policy also redacts plan values to SHA-256 digests. Unreadable destinations, incomplete rollback, skipped/refused redirects or policy denial exit with code 5. With --view both, text and JSON contain separate 32-bit and 64-bit changes, failures, policy denials and rollback paths. Unredacted JSON before/after states also contain an exact registry-value object with typed data or numeric type ID and raw bytes. When policy requires redaction, only the existing SHA-256 evidence is emitted and no exact payload is exposed.

A saved plan uses schema v1 and binds its payload, every named source file, each per-view desired mutation, and the relevant current registry state with SHA-256. apply-plan rechecks all bindings and current administrative policy before writing, then persists fresh per-view undo snapshots and applies with audited cross-view rollback. Any source or current-state drift exits 5 without mutation. Stdin plans cannot be saved because the source cannot be re-read. JSON output identifies a successfully persisted artifact with savedPlan, its exact byte length, and SHA-256; all three fields are null when no artifact was requested or a blocked/incomplete plan was not written.

batch

Apply ordered registry operations as one compensation transaction. The versioned batch schema gives every operation a unique ID and embeds the same explicit JSON key/value representation used by conversion.

powershell
> regx batch rollout.batch.json --conflicts error --view both --backup rollout.undo.reg -y --output json
> regx batch rollout.batch.json --dry-run --output json

Before the first mutation, regx validates policy and captures every target in every selected view. --conflicts error also rejects key/value collisions introduced inside an operation by Smart Redirection before that shared snapshot is read. The first failed operation stops the remainder and rolls all touched views back to that shared pre-batch state. JSON reports each ID as applied, planned, skipped, notAttempted, rolledBack, or rollbackFailed, together with per-view apply details and rollback results. Machine output identifies the separate batch result schema v1. Every per-view undo entry includes its path, exact byte length, and SHA-256. Dry-run keeps the planned entries with null evidence; live and offline-hive batches use the same contract.

audit and the audit trail

A tool that changes the registry and leaves no attributable record of what it changed cannot be deployed in a managed environment. --audit-log appends one JSON object per mutation — timestamp, actor SID, operation, before and after — to a file you nominate.

powershell
# Every change recorded, then verified
regx import app.reg --audit-log "C:\logs\regx.jsonl"
regx audit "C:\logs\regx.jsonl"
regx audit "C:\logs\regx.jsonl" --rotate-to "C:\logs\regx-001.jsonl"
regx audit "C:\logs\regx-001.jsonl" --chain "C:\logs\regx.jsonl"
regx audit "C:\logs\regx.jsonl" --write-anchor "X:\anchors\regx.anchor"
regx audit "C:\logs\regx.jsonl" --verify-anchor "X:\anchors\regx.anchor"
regx audit "C:\logs\regx.jsonl" --write-anchor "X:\anchors\regx.anchor" --anchor-key "X:\keys\anchor.key"

  records   7
  sessions  3
  sha256    78992f9622a90cd781a9cb442c6251105548f420e13dbc5abeef4c92a0a7469c

  Chain intact: no record has been altered or removed.

Rotation refuses a broken log or an existing archive. The new segment begins with a hashed segment.start record that binds the previous tail hash and archive SHA-256. --chain verifies segments in chronological order and detects editing, omission, or reordering. Run rotation while regx writers are quiescent; this CLI is not a logging service.

--write-anchor atomically stores the log SHA-256, tail hash, and record count in a detached checkpoint. --verify-anchor reports internal-chain integrity and checkpoint equality separately. Keep the anchor on another host, append-only storage, or a signed change ticket; placing it beside the writable log does not create a separate trust boundary. --anchor-key writes a v2 HMAC-SHA256 checkpoint and requires the same 32-byte-or-longer raw secret during verification. Signed anchors reject missing or wrong keys, and keyed verification refuses unsigned v1 input to prevent downgrade. Protect the key with ACLs and keep it on a separate trust boundary where possible. JSON write results include the exact byte length and SHA-256 of the persisted rotation archive or detached anchor. Dry-run uses null evidence because no artifact exists yet.

Set REGX_AUDIT_LOG to enforce it machine-wide, so an individual invocation cannot skip the trail by forgetting the flag. REGX_AUDIT_REDACT does the same for redaction.

Why the records are chained

A log an attacker can quietly edit is not evidence. Every record carries the SHA-256 of the previous record, so altering or removing a line breaks the chain from that point and regx audit reports the line number and what it expected.

What this does and does not give you. It makes silent tampering detectable. It does not stop someone truncating the tail or rewriting the file wholesale — nothing held locally can, without a key the operator does not have. Ship the log somewhere append-only for that half of the problem. The chain is what turns "someone may have edited this" into "this line was edited".

Redaction

Registry values hold licence keys, tokens and connection strings, so a log that faithfully records every byte written becomes a secret in its own right. --audit-redact records the SHA-256 and byte length of each value instead of the value — still enough to prove a specific value was written, or to compare two runs.

Redaction covers the recorded command line too. regx set … -d SECRET would otherwise put the secret straight into the session header, which is exactly the hole an early version of this had: the values were redacted and the command line was not.

A --dry-run is recorded with outcome simulated, so a rehearsal is distinguishable from the real thing in the record. Failed operations are recorded too, with the error — an attempt that was denied is as much a part of the trail as one that succeeded.

lnk: Known Folders and native Windows shortcuts

Every path-bearing CLI argument recognizes shell:Startup, shell:Desktop, and shell:Programs. regx resolves these names with SHGetKnownFolderPath, with the documented legacy SHGetFolderPathW fallback. It does not expand a guessed environment path or invoke an external shell.

powershell
# Create a current-user Startup shortcut through native COM
> regx lnk create --target "C:\Program Files\Acme\Acme.exe" --output "shell:Startup\Acme.lnk" --args=--background --workdir "C:\Program Files\Acme" --icon "C:\Program Files\Acme\Acme.exe,0" --style hidden -y

# Inspect without launching, rehearse, then remove
> regx lnk inspect "shell:Startup\Acme.lnk" --output json
> regx lnk delete "shell:Startup\Acme.lnk" --dry-run
> regx lnk delete "shell:Startup\Acme.lnk" -y

Creation uses CoCreateInstance(CLSID_ShellLink), IShellLinkW, and IPersistFile. Target, arguments, working directory, description, icon location/index, and show style are written to a temporary link, read back through COM, verified, and atomically committed. The hidden and minimized styles both request SW_SHOWMINNOACTIVE; this controls initial window presentation and does not conceal the Startup entry from Windows or regx.

All mutations support confirmation, -y, --dry-run, and tamper-evident --audit-log records containing exact before/after shortcut SHA-256 values. A shortcut target must be an existing absolute file, the destination must end in .lnk, and inspect/delete refuse objects that cannot be parsed as native links.

Shortcut manifests

lnk apply reads UTF-8 or BOM-marked UTF-16 from a file, -, or pipe:NAME. It preflights every block, rejects duplicate destinations, asks once, and rolls earlier writes back if a later action fails. Stream mutations require -y because the stream is already consumed before confirmation.

startup.shortcuts
[SHORTCUT]
Target=C:\Program Files\Acme\Acme.exe
Output=shell:Startup\Acme.lnk
WorkingDirectory=C:\Program Files\Acme
Arguments=--background
Description=Acme background client
Icon=C:\Program Files\Acme\Acme.exe,0
Style=hidden

[DELETE_SHORTCUT]
Path=shell:Desktop\Old Acme.lnk
powershell
> regx lnk apply startup.shortcuts --dry-run --output json
> Get-Content startup.shortcuts -Raw | regx lnk apply - -y

Automation can consume the shortcut result schema v1, the machine-readable capability inventory, or the compact AI-agent project summary.

Input formats

import, convert, sync and inspect all accept any format below. Each reader produces the same internal model, so redirection, coalescing, undo snapshots and apply behave identically whatever the input was. The formats command prints the same inventory from the executable, including detection notes, and supports --output json for tooling.

powershell
> regx formats
> regx formats --output json
FormatTypical fileNotes
reg.regUTF-16 Version 5.00 or ANSI REGEDIT4.
polRegistry.polGroup Policy PReg binary. See below.
admx.admx + .admlPolicy template. Concrete values are read; administrator-supplied elements are fidelity losses.
gppRegistry.xmlGroup Policy Preferences. Value R/U, all D, and key C/U are modeled; value C, key R, and targeting are fidelity losses.
inf.inf[AddReg]/[DelReg] with [Strings] substitution.
json.jsonCompact or explicit form.
csv.csv, .tsvHeader-driven columns, delimiter auto-detected.
ini.ini, .cfgSection per key path.
hiveNTUSER.DATDetected, then redirected to regx hive.

Detection reads content first and the extension second. Override it with --from <FORMAT> when a file is mislabelled.

Registry.pol

The binary a domain controller pushes down and the Group Policy engine replays. The cached copies under %WINDIR%\System32\GroupPolicy and …\GroupPolicyUsers are readable by ordinary users, so you can see exactly which registry writes a policy performs — no elevation, no guessing.

DirectiveHandled as
**del.Namedelete the value Name
**delvals.delete every value while preserving subkeys — reported as a fidelity loss; conversion and mutation fail closed because a key delete would be more destructive
**DeleteValuesexpands the ;-separated list into individual value deletes
**DeleteKeysexpands into [-KEY] blocks for each named subkey
**soft.Name"write only if absent" — reported as a fidelity loss; conversion and mutation fail closed rather than overwriting an existing value
**SecureKey, **ListElementno registry effect; ignored and reported

A .pol records no hive. The same bytes mean HKLM under Machine\ and HKCU under User\, so the root has to come from outside the file. regx infers it from the path and falls back to --pol-root; the choice is always printed.

ADMX and ADML

An ADMX is a schema, not data: it declares which registry values a policy controls, not what an administrator chose. That distinction drives what is emitted.

Emitted, because it is concrete:

  • <enabledValue> / <disabledValue> on the policy's own valueName
  • the documented ADMX default when neither is declared — enabling writes REG_DWORD 1, disabling writes REG_DWORD 0
  • <enabledList> / <disabledList> items, which carry literal values and may override the key

Reported, never emitted: <elements>text, decimal, boolean, enum, list, multiText. Those hold whatever an administrator typed into the Group Policy editor. Inventing a value for them would put fabricated data into the registry, so regx inspect lists them as fidelity losses. Conversion and mutation fail closed whenever such a loss exists. To see what was actually configured, read the Registry.pol instead.

class picks the hive: Machine → HKLM, User → HKCU, Both → emitted into both, which is what Windows does. An accompanying .adml in a language folder is found automatically and resolves the $(string.Id) display names. Choose the rendered state with --admx-state enabled|disabled and narrow to one policy with --admx-policy.

Group Policy Preferences

The other half of Group Policy. Where an ADMX declares a schema and a Registry.pol carries the policy branch, GPP writes anywhere in the registry — and its writes are not reverted when the GPO stops applying unless the item says so. That is frequently why a setting keeps coming back.

Unconditional Replace and Update value writes are modeled, as are all deletes and idempotent key Create/Update actions. The protocol's default="1" attribute distinguishes a default value from a key-only item; an empty name alone means the key. Value Create depends on absence, key Replace deletes the complete subtree before recreation, bitfield/SubProp writes depend on the current DWORD, item-level targeting depends on the client environment, and removePolicy="1" requires a future undo when the GPO leaves scope. Those cases are fidelity losses and block conversion/mutation rather than being flattened. <Collection> groups are traversed, REG_MULTI_SZ entries are read from the <Values> children rather than the value attribute, and items under an outer RegistrySettings disabled="1" are skipped as a disabled preference type. Non-schema item-level disabled attributes are rejected. The reader traverses only the protocol's RegistrySettings/Collection/Registry grammar; registry-looking descendants under unrelated XML wrappers are not accepted. Content detection uses that parsed root too, so renamed valid fragments remain detectable without classifying arbitrary XML by a nested tag substring.

Both XML readers refuse a DOCTYPE rather than skipping it. A DOCTYPE is where external-entity and billion-laughs attacks live, and neither format needs one — an XML parser that resolves external entities in a tool reading files from a policy share is an XXE vulnerability waiting to happen. Nesting depth is bounded too.

INF

Reads every [AddReg] and [DelReg] section named by an AddReg=/DelReg= directive; restrict it with --inf-section. %Token% references resolve against [Strings], and %% stays a literal percent. Microsoft requires every token to be defined, so an unknown or unterminated token is reported as a fidelity loss instead of being turned into registry data Windows would not have installed. For international INF files, --inf-language 0409 selects a four-digit Windows LANGID and follows SetupAPI's exact, neutral, same-language-family, then undecorated [Strings] fallback order. Without the option, the undecorated section is selected deliberately rather than depending on the workstation locale. Physical lines ending in an unquoted \ are joined before parsing. Quoted token definitions preserve edge whitespace and semicolons, collapse "" to one quote, and reject duplicate or unterminated definitions instead of silently choosing one. HKR is a fidelity loss because it is relative to a driver install context that only exists inside SetupAPI. NOCLOBBER, APPEND, OVERWRITEONLY, and per-line 32/64-bit view flags likewise require state or routing absent from the common model, while a referenced [Section.security] changes ACLs. They block conversion and mutation. Exact key-only and delete flags remain usable, and custom binary registry types encoded as 0xTYPE0001 retain their numeric type ID and bytes.

JSON

The compact form is what you write by hand. Types map on sight.

acme.json
{
  "HKCU\\Software\\Acme": {
    "Server":  "acme.test",            // REG_SZ
    "Port":    8080,                   // REG_DWORD
    "Enabled": true,                   // REG_DWORD 1
    "Recent":  ["a.txt", "b.txt"],   // REG_MULTI_SZ
    "Blob":    { "type": "REG_BINARY", "data": "de ad be ef" },
    "Legacy":  null                    // delete the value
  }
}

An integer too wide for a DWORD widens to REG_QWORD rather than truncating. Floating-point numbers are refused: the registry has no such type, and silently rounding would be worse than an error. Registry paths need doubled backslashes"HKCU\\Software", not "HKCU\Software" — because a lone backslash is not a valid JSON escape.

The explicit form adds key deletion and named types:

explicit form
{ "keys": [
  { "path": "HKCU\\Software\\Gone", "delete": true },
  { "path": "HKCU\\Software\\Acme",
    "values": [ { "name": "Port", "type": "REG_DWORD", "data": 8080 } ] }
] }

CSV

Columns are matched by header name in any order, case-insensitively, so a sheet exported from Excel works unedited. Quoting follows RFC 4180. An empty type and data deletes the value; DELETE_KEY in the data column with no value name deletes the key.

acme.csv
key,name,type,data
HKCU\Software\Acme,Server,REG_SZ,acme.test
HKCU\Software\Acme,Port,REG_DWORD,8080
HKCU\Software\Acme,Legacy,,                 # delete the value
HKCU\Software\Old,,,DELETE_KEY              # delete the key

INI

A section header is a full registry path and [-Path] deletes the key, both borrowed from .reg. The optional :type suffix is the only addition; without it a value is REG_SZ, which is what an ordinary INI means anyway. REG_MULTI_SZ entries are separated with |, which reads better here than reg.exe's \0.

acme.ini
[HKEY_CURRENT_USER\Software\Acme]
Server = acme.test
Port:dword = 8080
Path:expand_sz = %USERPROFILE%\acme
Recent:multi_sz = a.txt|b.txt
Blob:binary = 01 02 ff
Legacy =                       ; empty value deletes it
@ = default value

[-HKEY_CURRENT_USER\Software\Old]

Redirection flags

Shared by import, convert, sync and set.

FlagValuesMeaning
--redirect off, auto, classes-only, force Default auto. classes-only maps just Software\Classes, the one mapping reliable by design. force implies the lowest confidence floor.
--min-confidence high, medium, low Default medium. Mappings weaker than this are skipped and reported.
--on-refuse skip, fail What to do with keys that have no per-user equivalent at all. Default skip.

SOFTWARE\WOW6432Node\X is normalised to SOFTWARE\X before classification, so 32-bit and 64-bit exports of the same application collapse onto one destination. Because that collapse produces duplicate key blocks, every redirect run is followed by a case-insensitive coalesce pass.

Active Setup, User Shell Folders/Shell Folders and Winlogon are recognised explicitly and refused under HKLM. Their machine and user branches have different semantics: Active Setup's HKCU entry is a completion marker, known folders should target the existing user profile (preferably through the Windows Known Folder API), and Winlogon's Shell/Userinit values can make sign-in unusable.

Value types

set follows reg.exe conventions, so muscle memory transfers.

Type-d formatExample
REG_SZLiteral text-d "C:\Tools"
REG_EXPAND_SZText with environment variables-d "%USERPROFILE%\bin"
REG_MULTI_SZEntries separated by a literal \0-d "a.txt\0b.txt"
REG_DWORDDecimal or 0x hex-d 25 or -d 0x19
REG_QWORDDecimal or 0x hex-d 0xdeadbeef
REG_BINARYHex digits, separators optional-d "01 02 03"

Getting past AppLocker, SRP and WDAC

UAC is not the real obstacle in a locked-down enterprise; application control is. An unsigned .exe under %TEMP%, Downloads or %APPDATA% is precisely the shape the default rule sets deny. regx --self-check reads the relevant policy keys — all readable by a standard user — and reports what applies.

  1. Sign the binary

    A publisher rule follows the file anywhere; a path rule does not. An EV certificate additionally carries immediate SmartScreen reputation, which a standard OV certificate has to accumulate. In a managed environment a certificate from the organisation's internal CA is usually faster to obtain and already trusted domain-wide.

  2. Run from a path the policy already allows

    Typically %ProgramFiles% or an IT-managed share, rather than Downloads.

  3. Clear the Mark-of-the-Web

    Unblock-File removes the Zone.Identifier stream that triggers the SmartScreen interstitial.

signing
signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /a regx.exe

Always timestamp with /tr — without it, signatures stop validating when the certificate expires.

WDAC is different. It ignores file location entirely. If a user-mode code-integrity policy is deployed, only a signature or an explicit hash rule will let the binary run — steps 2 and 3 do not help.

.reg format notes

Behaviours that surprise people writing .reg files by hand, all of which regx matches to regedit:

  • Two escapes only. \\ and \". There is no \n or \t — writing one produces a literal backslash followed by that character.
  • Two encodings. Windows Registry Editor Version 5.00 is UTF-16LE with a BOM; REGEDIT4 is ANSI in the machine's codepage. A REGEDIT4 file written on one codepage decodes differently on another.
  • Continuations are hex-only. A trailing \ continues a hex payload. A string value ending in "C:\\" is not a continuation.
  • Strings need terminators. hex(1), hex(2) and hex(7) payloads must be NUL-terminated — REG_MULTI_SZ doubly so. Without them the consuming application reads past the value.
  • Multiline or embedded-NUL REG_SZ remains lossless. Because quoted .reg strings cannot escape those controls, the writer automatically emits NUL-terminated UTF-16LE hex(1) instead.
  • Names are checked before Win32 or the writer sees them. Embedded NUL is rejected rather than silently truncating a name; line-breaking control characters are refused because .reg has no name escape for them. Key components and value names are limited to 255 and 16,383 UTF-16 code units respectively.
  • Comments are line-initial. A ; starts a comment only at the beginning of a line, and its behaviour inside a hex continuation is inconsistent in regedit itself.

regx preserves values it cannot model losslessly as raw hex(N) bytes, and only writes a REG_SZ as a quoted string when it is clean UTF-16 — even length, single trailing NUL, no embedded NUL, no control characters. A raw newline inside a quoted string would corrupt the next line of the file.