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 ./...