UNF*

Your AI agent just wiped your workspace. You didn't commit.

UNF* recorded every version. Rewind any file to any second.
No commits required.

Open source · MIT / Apache-2.0 · Star us on GitHub

Install in seconds with one command.

# macOS (universal binary, includes CLI) $ brew install --cask cyrusradfar/unf/unfudged
# macOS + Linux $ brew install cyrusradfar/unf/unf
$ curl -fsSLO https://downloads.unfudged.io/releases/latest/unf_arm64.deb $ sudo dpkg -i unf_arm64.deb
$ curl -fsSLO https://downloads.unfudged.io/releases/latest/unf_amd64.deb $ sudo dpkg -i unf_amd64.deb
# Requires Rust 1.80+ $ git clone https://github.com/cyrusradfar/homebrew-unf.git $ cd homebrew-unf $ cargo build --release $ cp target/release/unf /usr/local/bin/
Read the docs

Files disappear between commits.

An AI agent rewrites 47 files. You weren't watching.
You delete the wrong branch. No backup.
A refactor breaks everything. The working version is gone.
You close the editor. It saved over the good version.

Git can't help. You didn't commit.

Ctrl+Z can't help. You closed the editor.

Time Machine can't help. It runs hourly.

UNF* records continuously. Every save, every second.

If your filesystem saw it, UNF* has it.

$ unf watch Watching /Users/agent/myapp ... 2h of work, context compacts, agent resumes ... $ unf recap --since 3h -- /Users/agent/myapp -- Session 1 14:02 - 16:18 (2h 16m, 847 edits, 23 files) git: main 5 uncommitted files Last edit: tests/fixtures/seed.json 3 minutes ago # agent: I remember the auth refactor. Not the seed edit. # One-char change — let me see before I submit. $ unf diff --at 30m tests/fixtures/seed.json - "email": "alice@example.com" + "email": "alicia@example.com" # agent: tests/auth/session_test.rs asserts "alice". # That "typo fix" would break the test. Reverting. $ unf restore --at 30m tests/fixtures/seed.json Restored tests/fixtures/seed.json

Real unf output. Agent flow after context compaction.

How it works
01
WATCH

FSEvents on macOS, inotify on Linux. Respects .gitignore by default, with an opt-in override. Skips binaries. 3-second smart debounce, capped at 15 seconds so sustained bursts still land while they are running.

<1% CPU · <100MB RAM

02
STORE

BLAKE3 content hashing. SQLite with ACID guarantees. Content-addressed, so identical files are stored once. Local-only, always.

03
REWIND

Point-in-time restore for any file, any second. Manual pruning with unf prune to reclaim space.

Read the technical deep dive
Commands
unf watchStart recording the current directory
unf unwatchStop recording (history preserved)
unf statusWatcher status + snapshot stats
unf log <file>Timeline of every version of a file
unf log --since / --untilFilter log to a time range
unf diff --at "5m"What changed in the last 5 minutes
unf restore --at TIMERewind files to any point in time
unf cat --at TIMEView file contents at a point in time
unf recapSummarize session activity + context
unf listShow all watched directories
unf pruneRemove snapshots older than a cutoff you choose
unf prune --all-projectsSame across every project. Cannot be undone; asks first if it would empty a project
unf stop / restartControl the background daemon
unf configShow storage location and disk usage
unf config --move-storageMove storage to a new location

Time formats

Relative: 5m 2h 1d

Local: "2026-08-25 21:10:03" — exactly what unf log prints, trailing -0600 offset and all. Quote it, or the shell reads the offset as a flag.

UTC: 2026-08-25T21:10:03Z

Seconds are required. 2026-08-25 and 2026-08-25 21:10 are rejected.

Global flags

--json — machine-readable output for all commands

--project <PATH> — operate on any project from anywhere

What gets recorded

All text files, recursively. Respects .gitignore. Skips binaries by magic-number detection. Skips build and dependency directories: .git, node_modules, target, dist, build, and five more. They are large and they change on every build.

Run unf watch --force-watch-gitignore to also record gitignored files and hidden dotfiles. Secrets in those files, such as .env.local, go into the recording.

Run unf watch --unignore-dir target to record one skipped directory in one project. Most projects also gitignore those directories, so pass both flags together to record them.

The same flag records three paths inside .git that git itself does not track: unf watch --unignore-dir .git/hooks, plus .git/config and .git/info/exclude. Cost is low, unlike target: the 14 default hooks are inert .sample files that never change. Everything else under .git stays refused, including the object store and the refs. Submodules are not covered.

Storage

Defaults to ~/.unfudged/. Move anywhere with unf config --move-storage. Nothing inside your project directory. Content-deduplicated.

Install
# macOS (universal binary, includes CLI) $ brew install --cask cyrusradfar/unf/unfudged
# macOS + Linux $ brew install cyrusradfar/unf/unf
$ curl -fsSLO https://downloads.unfudged.io/releases/latest/unf_arm64.deb $ sudo dpkg -i unf_arm64.deb
$ curl -fsSLO https://downloads.unfudged.io/releases/latest/unf_amd64.deb $ sudo dpkg -i unf_amd64.deb
# Requires Rust 1.80+ $ git clone https://github.com/cyrusradfar/homebrew-unf.git $ cd homebrew-unf $ cargo build --release $ cp target/release/unf /usr/local/bin/

Then: unf watch. That's it.

Fast <1% CPU. You won't notice it.
Local-first No cloud. Nothing leaves your machine.
Free No subscription. No limits.
Secure No accounts. No telemetry. No network.
Use cases