Invisible, auto-syncing personal storage for any git repo
Project description
Swarf
Invisible, auto-syncing personal storage for any git repo.
You build software and generate byproduct — research docs, design specs, agent skills, scratch notes. Swarf gives this material a durable home alongside any project, without touching the project itself.
The problem
You have files that should live near your code but don't belong in the repo:
- Agent instructions and skills (
AGENTS.md,.copilot/skills/) - Research notes, design docs, open questions
- Security audits, experiment logs, scratch work
Today these files are either untracked and local-only (one rm -rf from
gone) or committed to the repo (polluting history, leaking on public
repos). Neither is good.
How it works
Swarf creates a swarf/ directory inside your project. Everything in it is
automatically excluded from git via .git/info/exclude. A background daemon
watches for changes, mirrors them to a central store, and syncs to your
configured remote (a git repo or any rclone backend).
my-project/
├── src/
├── swarf/ ← private storage (invisible to git)
│ ├── docs/ ← research, design, anything
│ ├── .links/ ← files projected into the host tree
│ │ └── AGENTS.md
│ └── open-questions.md
├── AGENTS.md → swarf/.links/AGENTS.md ← symlink (also gitignored)
└── .git/info/exclude ← managed by swarf
The central store at ~/.local/share/swarf/ mirrors all projects into a
single git repo. The daemon commits changes locally, then pushes to your
remote. For rclone backends, the entire store is synced — both working
files and .git/. This means you can browse your files directly in Google
Drive (or whatever backend), and you get full commit history too.
Install
# macOS / Linux (recommended)
brew install swarf
# Via PyPI (persistent install to ~/.local/bin/)
pipx install swarf # or: uv tool install swarf
# Via Go
go install github.com/mschulkind-oss/swarf@latest
# From source
git clone https://github.com/mschulkind-oss/swarf && cd swarf
just deploy # builds and copies to ~/.local/bin/
Warning: Don't use
pip install swarfinside a virtualenv oruvx swarf. The daemon records the binary's absolute path and breaks when the venv disappears.swarf doctordetects this and warns you.
Quick start
cd ~/projects/my-app
swarf init
On first run, init walks you through setup:
No global config found. Let's set one up.
Backend [git/rclone] (git): git
Remote URL (your private backup repo): git@github.com:you/my-swarf.git
✓ Wrote ~/.config/swarf/config.toml
✓ Created central store at ~/.local/share/swarf
✓ Initialized swarf/ for my-app
Install systemd service for auto-sync? [Y/n] y
✓ Installed systemd service — daemon is running
✓ Daemon is running (PID 12345)
✓ All checks passed.
Next time you run swarf init in another project — no prompts, instant setup.
Rclone backend (Google Drive, Dropbox, S3, etc.)
Set up an rclone remote first:
brew install rclone
rclone config
# → n (new remote), name: gdrive, type: drive
# → scope: drive.file (option 2 — only files rclone creates)
# → auto config: y (opens browser for OAuth)
Then run swarf init, pick rclone, and select your remote from the
numbered menu. Swarf defaults to swarf-store as the directory path:
Backend [git/rclone] (git): rclone
Pick an rclone remote:
1. gdrive:
Enter a number (1-1), or q to quit: 1
Directory path on gdrive: [swarf-store]:
✓ Remote: gdrive:swarf-store
The store is always a local git repo. Rclone syncs the whole store to
the remote — your files are browseable directly in Google Drive (or
wherever), and you get full git history via the .git/ directory.
Using swarf
Drop files in
echo "# Design Notes" > swarf/docs/design.md
That's it. The daemon commits and syncs after a 5-second quiet period.
Sweep files into the host tree
Some files need to appear at a specific path in the project (like AGENTS.md
in the root). Use sweep to move them into swarf while leaving a symlink:
swarf sweep AGENTS.md
# AGENTS.md → swarf/.links/AGENTS.md
swarf sweep CLAUDE.md .copilot/skills/SKILL.md
Both the original path and swarf/ are automatically gitignored. Symlinks
are relative, so they work across machines and inside containers.
To reverse a sweep:
swarf unlink AGENTS.md
Auto-sweep
Configure files to be swept automatically whenever they appear:
# ~/.config/swarf/config.toml
[auto_sweep]
paths = ["AGENTS.md", "CLAUDE.md", ".copilot/skills/"]
The daemon watches for these files and sweeps them on creation.
Check status
swarf status
Shows store state, pending files, remote verification, and daemon health:
Store
Path ~/.local/share/swarf
Backend git
Remote git@github.com:you/my-swarf.git
Pending all synced to remote
Last commit 2 minutes ago — auto: sync 3 files
Local save 2m ago
Remote push 2m ago
Remote sync verified — local and remote match (a1b2c3d4)
Projects
╭──────────┬─────────────────────┬──────────╮
│ PROJECT │ PATH │ STATUS │
├──────────┼─────────────────────┼──────────┤
│ my-app │ ~/projects/my-app │ ✓ ok │
│ api │ ~/work/api │ ✓ ok │
╰──────────┴─────────────────────┴──────────╯
Daemon: running (PID 12345)
Doctor
swarf doctor checks everything and fixes what it can:
swarf doctor
Doctor handles: missing config, missing store, broken symlinks, absolute symlinks, missing gitignore entries, service installation, and remote reachability. Errors include the exact fix command or config file to edit.
init and doctor share the same engine — the difference is that init
creates swarf/ in a new directory, while doctor only checks and repairs.
Commands
| Command | Description |
|---|---|
swarf init |
Set up swarf in the current project |
swarf sweep <file>... |
Move files into swarf/.links/ and symlink back |
swarf unlink <file>... |
Reverse a sweep — restore symlinks to regular files |
swarf doctor |
Check health and fix problems (config, store, service, links) |
swarf status |
Show projects, sync state, remote verification, daemon health |
swarf clone |
Clone the store from your configured remote (new machine setup) |
swarf pull |
Pull latest changes from the remote into the store |
swarf daemon start |
Start the background sync daemon (--foreground for debugging) |
swarf daemon stop |
Stop the daemon |
swarf daemon status |
Check if the daemon is running |
swarf daemon install |
Install as system service (systemd on Linux, launchd on macOS) |
swarf docs [topic] |
Browse built-in documentation |
Second machine setup
brew install swarf
swarf clone # clones your store from the configured remote
cd ~/projects/my-app
swarf init # re-links the project from the store
clone requires global config to exist (with the remote URL). After cloning,
run swarf init in each project directory to recreate the local swarf/
directory and symlinks.
Containers and jails
The daemon runs on the host. Containers mount the project directory, so
swarf/ comes along. Agents read and write to swarf/ directly — the host
daemon picks up changes and syncs.
Inside a container, swarf doctor detects the environment (no global config),
skips system checks, and validates project-local state only. sweep and
unlink work without the daemon. Symlinks are relative, so they resolve
correctly regardless of mount path.
Guides
- Configuration Guide — all config options, file locations, environment variables, and examples
- Roadmap — planned features and distribution channels
- Built-in docs:
swarf docslists all topics (quickstart,architecture,config,sweep,daemon,backends)
License
Apache 2.0
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swarf-0.4.0-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: swarf-0.4.0-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 6.1 MB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98969534126774320130174a0c73bce1b08f67aa06e6d74d543f052648d6dd48
|
|
| MD5 |
54d3a645425ef7478ddc6412bfd9e1a9
|
|
| BLAKE2b-256 |
8ed3204ee9c23a92a84d9ce1b0fb0aab39fbdacc3c3dad84620efc4391ba3fd8
|
File details
Details for the file swarf-0.4.0-py3-none-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: swarf-0.4.0-py3-none-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 5.8 MB
- Tags: Python 3, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb5aa64614cb33fe842736a8a6de2dcec2af476ccbf9b88d5ac99264bba0bb8f
|
|
| MD5 |
0cd33053558f9f92145859dbd9dd8921
|
|
| BLAKE2b-256 |
92931b76f7781baca126264b02e15dd24e863d5940f0388d7003ea5bd7c4edd8
|
File details
Details for the file swarf-0.4.0-py3-none-manylinux_2_17_x86_64.whl.
File metadata
- Download URL: swarf-0.4.0-py3-none-manylinux_2_17_x86_64.whl
- Upload date:
- Size: 6.1 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5d8d7baf3a2116fd9d62e4361c8926f581c5da274e624153ad85f5d8be743a9
|
|
| MD5 |
6c8a69120be58d648b76ab6907fa557c
|
|
| BLAKE2b-256 |
0457acbeebbca675379b7fabedb1ead7ec5b1afe0540526f77db553ec7bb2246
|
File details
Details for the file swarf-0.4.0-py3-none-manylinux_2_17_aarch64.whl.
File metadata
- Download URL: swarf-0.4.0-py3-none-manylinux_2_17_aarch64.whl
- Upload date:
- Size: 5.8 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e5aa66ad0bed35463b3c40b92becf84187a8d8d929c7f4cf8e51c0672bfd14f
|
|
| MD5 |
aefc3bd536580c2844b1cdaea391fa3d
|
|
| BLAKE2b-256 |
60655d19cd9f503a957da0bcd698a15886cffd60777cccac4b0332c9ba863060
|
File details
Details for the file swarf-0.4.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: swarf-0.4.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 5.9 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d28534bae2c21eee6969c5846173dd1c4bb25638b712e04ad273b9e61ac07a4
|
|
| MD5 |
416b184a4f1bed0c2f206f6243af128b
|
|
| BLAKE2b-256 |
58c841e11e859269062efab1c67eb885cc628b5366eda819d42b9f643665b602
|
File details
Details for the file swarf-0.4.0-py3-none-macosx_10_9_x86_64.whl.
File metadata
- Download URL: swarf-0.4.0-py3-none-macosx_10_9_x86_64.whl
- Upload date:
- Size: 6.3 MB
- Tags: Python 3, macOS 10.9+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f4d92461993683c503470ec0e2172a77ea3857935d9566d5d525b676ba81605
|
|
| MD5 |
2b23a0ed09761efd8766329167a39f7b
|
|
| BLAKE2b-256 |
3155ab627f3a52c1453f81cfa709bc5c971c9dd1aad10a3b9ae1400dfa40545e
|