jjfs
Version, currently 0.1.02 versions
- 0.2.0latestOct 19, 2025
- 0.1.0not indexedOct 19, 2025
github.com/jtippett/jjfs
No description declared in shard.yml.
Nothing has been indexed for 0.1.0 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.
Installation
# Add this to your shard.yml
dependencies:
jjfs:
github: jtippett/jjfs
version: ~> 0.1.0Then run:
shards installshard.yml
No shard.yml has been indexed for 0.1.0. You can read it on the repository.
Dependencies
Unknown: the shard.yml for this version has not been read yet.
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.
jjfs - Eventually Consistent Multi-Mount Filesystem
NFS-based filesystem that mounts multiple directories as live, eventually-consistent views of the same Jujutsu repository.
Features
- Multi-mount: Mount the same repo in unlimited locations
- Eventually consistent: Changes propagate automatically (<2s)
- Zero maintenance: Auto-syncs and auto-starts on login
- Remote backup: Push and pull to GitHub or GitLab
- Cross-platform: Runs on macOS and Linux
- Git-aware: Detects git repos and offers to update .gitignore
- No external dependencies: Built-in NFS server, no FUSE or kernel extensions required
- Easy repo management: Import existing git repos or create new jj repos with simple commands
Status
Version: 0.2.0 Status: Production ready (with known NFS unmount caveat on macOS)
jjfs provides a stable foundation for multi-location file synchronization using Jujutsu's workspace feature. The sync engine handles multiple concurrent mounts and resolves conflicts gracefully.
Installation
via Homebrew (macOS/Linux)
brew install jtippett/jjfs/jjfs
This installs both jjfs and jjfsd. Note: On macOS, you'll need to install bindfs separately (see caveats after installation).
Alternatively, tap first then install:
brew tap jtippett/jjfs
brew install jjfs
Build from Source
Requirements
- Crystal 1.10+
- Rust toolchain (for building jjfs-nfs)
- Jujutsu (
jj) - Install guide - macOS: fswatch (
brew install fswatch) - Linux: inotify (built-in)
Build Steps
# Clone the repository
git clone https://github.com/jtippett/jjfs.git
cd jjfs
# Install dependencies (if any)
shards install
# Build Rust NFS server
cd jjfs-nfs
cargo build --release
cp target/release/jjfs-nfs ../bin/
cd ..
# Build Crystal binaries
crystal build src/jjfs.cr -o bin/jjfs --release
crystal build src/jjfsd.cr -o bin/jjfsd --release
# Install to system (optional)
sudo cp bin/jjfs /usr/local/bin/
sudo cp bin/jjfsd /usr/local/bin/
sudo cp bin/jjfs-nfs /usr/local/bin/
Setup
# Install system service (starts daemon automatically)
jjfs install
# Or start daemon manually
jjfsd &
# Initialize default repo
jjfs init
Quick Start
# Create a new repo
jjfs new my-notes
# Or import an existing git repo
jjfs import https://github.com/user/notes.git
# View available repos
jjfs repos
# Open the repo in multiple locations (requires sudo password for NFS mount)
jjfs open my-notes ~/Documents/notes
jjfs open my-notes ~/Desktop/quick-notes
# Work in either location - changes sync automatically!
echo "Hello world" > ~/Documents/notes/hello.txt
# Wait ~2 seconds
cat ~/Desktop/quick-notes/hello.txt
# Output: Hello world
# View all mounts with status
jjfs list
# Close a mount when done
jjfs close ~/Desktop/quick-notes
Usage
Commands
Repo Management:
jjfs new <name> # Create new jj repo
jjfs import <git-url> [name] # Import existing git repo
jjfs repos # List all available repos
jjfs init [name] # Initialize a repo (default: "default")
Mount Management:
jjfs open <repo> [path] # Open repo at path (default: ./<repo>)
jjfs close <path> # Close mount at path
jjfs list # List all mounts with status
Daemon:
jjfs status # Show daemon status
jjfs start # Start daemon
jjfs stop # Stop daemon
Other:
jjfs sync [repo] # Force sync (default: all repos)
jjfs remote add <url> [--repo=name]
jjfs install # Install system service
How It Works
jjfs builds on Jujutsu's workspace feature to synchronize files across mounts. Each mount represents a jj workspace. When you change files, fswatch or inotify detects the changes and commits them. The system then runs jj workspace update-stale to propagate changes across workspaces. Every 5 minutes (configurable), the system pushes and pulls to remote repositories. Jujutsu preserves conflicts by inserting conflict markers into files. When you mount inside a git repo, jjfs detects this and offers to add the mount to .gitignore.
Configuration
Config file: ~/.jjfs/config.json
{
"repos": {
"my-notes": {
"path": "/Users/you/.jjfs/repos/my-notes",
"remote": "git@github.com:user/my-notes.git",
"sync_interval": 2,
"push_interval": 300
}
},
"mounts": [
{
"id": "workspace-id",
"repo": "my-notes",
"path": "/Users/you/Documents/notes",
"workspace": "/Users/you/.jjfs/repos/my-notes/workspaces/workspace-id"
}
]
}
Use Cases
Sync Notes Across Devices
Keep your notes synchronized on multiple devices with automatic backup to GitHub:
# On device 1
jjfs init notes
jjfs remote add git@github.com:user/notes.git
jjfs open notes ~/Documents/notes
# On device 2 (after cloning the git repo to ~/.jjfs/repos/notes)
jjfs open notes ~/Documents/notes
Multiple Views of Same Project
Work on the same codebase from different directories:
jjfs init project
jjfs open project ~/work/project-stable
jjfs open project ~/work/project-experimental
# Changes in either location sync immediately
Desktop + Mobile Workflow
Keep a quick-access folder in sync with your main workspace:
jjfs init todos
jjfs open todos ~/Documents/todos
jjfs open todos ~/Desktop/quick-todos
Troubleshooting
Daemon won't start
# Check if already running
jjfs status
# Check lock file
cat ~/.jjfs/daemon.lock
# Remove stale lock if needed
rm ~/.jjfs/daemon.lock
Mount appears empty
# Check if bindfs is installed
which bindfs
# Check mount status
mount | grep bindfs
# Try remounting
jjfs close /path/to/mount
jjfs open repo-name /path/to/mount
Changes fail to sync
# Check daemon is running
jjfs status
# Check daemon logs
tail -f ~/.jjfs/sync.log
# Force manual sync
jjfs sync repo-name
Conflicts
When you edit the same file in multiple mounts simultaneously, jj creates conflict markers:
<<<<<<< Conflict 1 of 1
%%%%%%% Changes from abc123
Content from mount A
+++++++ Contents of def456
Content from mount B
>>>>>>> Conflict 1 of 1 ends
Resolve conflicts by editing the file to keep the desired content, then save.
Architecture
jjfs consists of six main components:
- CLI (
jjfs): Handles user-facing commands - Daemon (
jjfsd): Runs as a long-running process managing mounts and sync - RPC: Uses JSON-RPC over Unix socket for CLI-daemon communication
- Mount Manager: Creates and destroys jj workspaces and bindfs mounts
- Sync Coordinator: Watches for changes, commits them, and propagates to other workspaces
- Remote Syncer: Pushes and pulls to git remotes periodically
Development
# Run tests
crystal spec
# Run integration tests (requires bindfs)
crystal spec spec/integration_spec.cr
# Build in debug mode
crystal build src/jjfs.cr -o bin/jjfs
crystal build src/jjfsd.cr -o bin/jjfsd
# Run daemon in foreground for debugging
./bin/jjfsd
License
MIT
Contributing
Contributions welcome! Please open an issue or PR.
Documentation
- User Guide - Detailed workflows and examples
- Implementation Plan - Technical design
Credits
Built with:
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.1.0- Tagged
- Oct 19, 2025
- Commit
80e1d36d26dc- Indexed
- not yet
Dependents
No indexed shard depends on this one yet.
Repository
github.com/jtippett/jjfs
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 17, 2026
- Synced
- Aug 17, 2026
- Versions
- 2