Skip to content

v0.7.0

Latest

Choose a tag to compare

@github-actions github-actions released this 02 Jun 14:07

TigerFS 0.7 turns the filesystem into a fully transactional workspace. Every write produces an entry in .log/, named savepoints survive across sessions in .savepoint/, and .undo/ reverses any operation, log entry, or savepoint with a single touch. Per-user filtering means multiple agents can collaborate on the same workspace without stepping on each other, and undo-of-undo closes the loop so changing your mind about a rollback is itself a one-touch action. The combined effect is that a coding agent can experiment freely against a database and the human can always trace back, attribute, or reverse exactly what changed.

This release also makes TigerFS noticeably more POSIX-faithful. Atomic-rename writers (Vim, Claude Write/Edit, any editor that swaps a tmp file into place) now work correctly; directory mtimes reflect real changes via a database-side trigger plus a new noac mount option; user-created dotfiles like .gitignore coexist with the .log/ and .savepoint/ control surface without collision. Schema modeling expanded too: tables with composite primary keys now mount cleanly (rows appear as region,user_id directories), the directory structure is parent-pointer-based so a rename is a single-row update, and SELECT-only database roles work end-to-end. Recursive scanners like rm -rf and find no longer infinite-loop in the virtual tree, and a longstanding source of EIO and ENOENT under load on Linux FUSE mounts is gone.

Upgrading from 0.6 requires tigerfs migrate. The migration adds parent_id, filetype, and filename columns to existing backing tables and rebuilds history triggers; it is idempotent and dry-run-safe. Undo of log entries created before the migration is blocked with EPERM because pre-migration history lacks the parent-pointer information that 0.7's undo needs to be correct; .log/ and .history/ remain readable for those older entries. Fresh 0.7 installs are unaffected.

Operation log, savepoints, and atomic undo

  • Operation log (.log/) -- every write produces a log entry capturing user, timestamp, before/after content, and operation type; browse via .log/.last/N/.export/json or index queries .log/.by/file_id/, .log/.by/user_id/, .log/.by/type/
  • Diff symlinks -- each log entry exposes before, current, and after virtual files so diff -u .log/<id>/before .log/<id>/current works directly
  • Savepoints (.savepoint/) -- named bookmarks created by writing JSON to .savepoint/<name>.json; survive across sessions and users
  • Auto-savepoints -- configurable per-mount inactivity gap (--auto-savepoint-interval, default 30 minutes) records bookmarks automatically at session boundaries
  • Undo (.undo/) -- three modes: .undo/id/<log_id>/.apply (single op), .undo/to-id/<log_id>/.apply (multi-file rollback to a log entry), .undo/to-savepoint/<name>/.apply (multi-file rollback to a savepoint); preview affected files via .undo/<mode>/<target>/.info/summary before applying
  • Per-user undo -- filter any undo by user via .by/user_id/<user>/.apply; agents only roll back their own changes in collaborative workspaces
  • Undo of undo -- undo operations are themselves logged, so you can reverse an undo by undoing its log entry; directory renames round-trip correctly even when batched with child file renames

Schema model and migration

  • Composite primary key support -- tables with multi-column PKs (e.g., PRIMARY KEY (region, user_id)) now mount cleanly; rows appear as comma-encoded directories like us,1, with , and / URL-escaped in PK values
  • Relational directory structure -- file/directory rows now reference their parent by id (UUID) rather than path string, so a directory rename is a single-row UPDATE that all children inherit (ADR-017)
  • tigerfs migrate updates -- new migrations move history triggers and add parent-pointer columns; idempotent and dry-run-safe

Filesystem behavior and correctness

  • POSIX rename-as-replace -- the atomic-rename pattern used by Vim, Claude Write/Edit, and most editors works correctly: rename(tmp, target) replaces target atomically instead of erroring
  • Directory mtimes via trigger + noac mount option -- directory mtimes reflect actual filesystem changes via a database-side trigger; the new noac mount option keeps the kernel from caching stale attributes across mounts of the same database
  • Real dotfiles in file-first workspaces -- user-created dotfiles (.gitignore, .editorconfig) coexist with the control-surface dotnames (.log/, .savepoint/, .undo/, etc.) without collision in markdown,history-built workspaces
  • Directory-listing recursion safety -- recursive scanners (rm -rf, find, agents probing structure) no longer infinite-loop on capability dirs; pipeline depth is capped and self-referential capability dirs are suppressed past a configurable limit
  • FUSE_INTERRUPT decoupling -- kernel interrupt signals (from SIGURG-driven Go preemption) no longer propagate as context.Canceled into the DB layer, eliminating spurious EIO/ENOENT under load on Linux FUSE mounts

Workspace identity and connectivity

  • Mount-level user identity via .info/user -- the mount's identity (used for per-user filtering on .log/ and .undo/.by/user_id/) is exposed as a virtual file; configure via --user, TIGERFS_USER, or the process owner's username
  • SELECT-only roles supported -- primary-key and unique-constraint discovery now reads pg_constraint instead of relations a read-only role can't access, so workspaces mounted with a SELECT-only DB user work end-to-end

Tools

  • tigerfs-stress harness -- new companion binary for soak-testing a workspace under deterministic, PRNG-seeded filesystem + undo workloads; ships with diagnostic dumps, end-of-run monotonicity reporting, and a Docker FUSE mode (./scripts/test-docker.sh)

Breaking Changes

  • 0.6 -> 0.7 history format migration. Run tigerfs migrate on workspaces upgraded from 0.6 -- it adds parent_id/filetype/filename columns to backing tables and rebuilds history triggers. The migration is idempotent. Undo of log entries created before the migration is blocked (EPERM) because pre-migration history has lossy parent_id information; .log/ and .history/ remain readable for those entries. Fresh 0.7 installs are unaffected.