Peer-to-peer file storage for an existing Headscale/Tailscale mesh.
The design is documented in AGENT.md. The deployed desktop watcher encrypts files, splits each chunk into 5 data shards plus 2 parity shards, distributes those shards across online peers, and writes a portable .dstore manifest that can restore the original file. New files are processed in bounded 16 MiB chunks so memory use does not grow with the file size; standalone commands retain the legacy 2+1 defaults unless their layout flags are set.
Important: the .dstore manifest contains the AES key. Anyone who receives the manifest and can reach at least 5 shard URLs for every version-2 chunk can recreate the original file.
- Go 1.24+
- Tailscale or Headscale running on each peer
- Each peer must have a unique hostname configured in Tailscale
Build the binary:
go build -o bin/dstore ./cmd/dstoreRun the agent (serves shards + watches for new files):
bin/dstore agent -origin outfiles -shards .dstore-shards -addr :8080To use specific shard nodes instead of random online Tailscale peers, pass -peers:
bin/dstore agent -origin outfiles -shards .dstore-shards -addr :8080 \
-peers node-a=100.64.0.10:8080,node-b=100.64.0.11:8080,node-c=100.64.0.12:8080The peer format is hostname=host[:port]. If fewer peers are listed than shard count, shards are assigned round-robin across those peers.
Drop a file or directory into outfiles/. The agent will process regular files recursively and leave .dstore manifests beside each original path:
- Encrypt it with AES-256-GCM
- Split each encrypted chunk into 7 Reed-Solomon shards (5 data + 2 parity)
- Randomly select online peers from your Tailscale mesh, or use the explicit -peerslist
- Push each shard to a peer via HTTP PUT
- Store a local copy as well
- Create filename.dstoreinoutfiles/
- Remove the original file
Install and start the agent:
./scripts/install-user-service.sh
systemctl --user start peertopeer-dstore
systemctl --user enable peertopeer-dstoreOr run directly:
bin/dstore agent -origin outfiles -shards .dstore-shards -addr :8080Drop any file into the outfiles/ directory on any peer. The agent automatically:
- Picks random online peers from your Tailscale mesh, or uses the configured -peersshard nodes
- Pushes shards to each peer's shard server
- Creates a .dstoremanifest inoutfiles/
Double-click (after desktop integration):
./scripts/install-desktop-integration.sh
xdg-open outfiles/example.txt.dstoreCLI:
bin/dstore restore -stub outfiles/example.txt.dstoreRestore fetches shards from the URLs in the manifest. Only 5 of 7 shards per chunk need to be reachable. If a peer is offline, the remaining shards are sufficient.
Email the .dstore file to another mesh member. They can restore it on any peer that can reach at least 5 of the 7 shard URLs for every chunk.
bin/dstore peers
bin/dstore peers -online-onlybin/dstore agent \
-origin outfiles \
-shards .dstore-shards \
-addr :8080 \
-base-url "" \
-interval 2s \
-peers node-a=100.64.0.10:8080,node-b=100.64.0.11:8080bin/dstore restore \
-stub outfiles/photo.dstore \
-shards .dstore-shards \
-output photo.jpgsystemctl --user start peertopeer-dstore # start
systemctl --user stop peertopeer-dstore # stop
systemctl --user status peertopeer-dstore # status
systemctl --user enable peertopeer-dstore # auto-start on login
journalctl --user -u peertopeer-dstore -f # follow logsTo run multiple storage peers on the same Linux machine as an existing
Headscale server, use the included Compose stack. It creates two separate
Tailscale nodes (peer-a and peer-b) by default with persistent identities
and storage; --profile extra adds peer-c and peer-d. No public dstore
ports are exposed.
The current erasure layout needs three ready storage peers for automatic file placement, so enable the extra profile or add a remote dstore node before using a peer as a file origin.
Create a reusable Headscale pre-authentication key, copy the environment template, and start the stack:
headscale users create dstore
headscale preauthkeys create --user dstore --reusable --expiration 24h
cp .env.peers.example .env.peers
# Set HEADSCALE_URL and HEADSCALE_AUTHKEY in .env.peers.
docker compose --env-file .env.peers -f docker-compose.peers.yml up -d --buildSee docs/docker-peers.md for verification, storage
paths, and the required /dev/net/tun Docker capability.
Install .dstore file association:
./scripts/install-desktop-integration.shAfter installation, double-clicking a .dstore file restores the original file.
go test ./...