Simple P2P File Sharing CLI Tool
- C++ 57.9%
- Python 26%
- Nix 12.2%
- CMake 3.3%
- PowerShell 0.6%
Support piping data through p2pshare using `-` as the path argument: - Sender: `cat file | p2pshare --batch - --link-file link.txt --peer-addr-file peer.txt` - Receiver: `p2pshare --batch 'link' -o - --link-file addr.txt > output` Replace the buffering TarExtractor (which held the entire archive in a std::vector) with a streaming pipe + background thread implementation. libarchive reads from the pipe's read end while the caller pushes data into the write end — memory is bounded by the OS pipe buffer (~64 KB). This allows receiving directories larger than available RAM. Other changes: - Progress bar: show spinner + bytes + speed when total is unknown - Early IPv6 availability check at startup - Pre-allocate output files with fallocate to catch disk space errors Testing: - Integration: add large directory transfer test (~2 GB via streaming tar on 1 GB RAM VMs with ext4-formatted virtual disks) - Integration: add stdin/stdout streaming test (2 GB raw block device piped through p2pshare, verified with SHA256) - O_DIRECT pseudo-random data generator to avoid page cache pressure in memory-constrained VMs - Unit: flush KCP after send_eof in large transfer test |
||
|---|---|---|
| .forgejo/workflows | ||
| src | ||
| tests | ||
| third_party/kcp | ||
| .clangd | ||
| .gitignore | ||
| CLAUDE.md | ||
| CMakeLists.txt | ||
| flake.lock | ||
| flake.nix | ||
| nfpm.yml | ||
| README.md | ||
p2pshare
A minimal, secure P2P file sharing CLI tool.
Features
- End-to-end encryption: AES-256-GCM (hardware-accelerated) with XSalsa20-Poly1305 fallback
- Compression: zstd streaming compression for efficient transfers
- NAT traversal: UDP hole punching with KCP reliable transport over the same socket
- IPv6 only: Modern IPv6 networking
- Directory sharing: Automatically creates tar archives for directories
- Reproducible builds: Nix flake support
Usage
Send a file
p2pshare myfile.txt
Send a directory
p2pshare -r ./mydir
Receive a file
p2pshare 'p2pshare://[fe80::1]:54321/<token>'
With custom output directory:
p2pshare -o ./downloads 'p2pshare://[fe80::1]:54321/<token>'
The mode is auto-detected: if the argument matches p2pshare://[ipv6]:port/token it receives, otherwise it sends.
Building
With Nix (recommended)
nix build
For a static build:
nix build .#static
With CMake
cmake -B build
cmake --build build
The executable will be at build/p2pshare.
How it works
- Sender generates a random 32-byte encryption key and 4-byte session ID
- Sender creates a share link containing the key and session ID (base62 encoded)
- Sender binds a UDP socket and displays its
p2pshare://[ipv6]:port/token - Receiver parses the share link and binds its own UDP socket
- Both peers exchange addresses out-of-band, then UDP hole punch to establish the connection
- Data transfer runs over KCP (reliable UDP) on the same hole-punched socket -- no new ports or TCP connections needed
- Sender streams the file: compress with zstd -> encrypt in 128KB chunks -> send via KCP
- Receiver receives: decrypt -> decompress -> write to disk
Protocol
Hole punch signaling uses an 8-byte header: [type:1][flags:1][seq:4][length:2]
Connection establishment uses a HELLO/PUNCH handshake with version and session ID verification. Data transfer runs over KCP (reliable UDP) with fast retransmission, selective ACKs, and no congestion control (optimized for direct peer-to-peer links).
Each data chunk is encrypted with AES-256-GCM (or XSalsa20-Poly1305 on CPUs without AES-NI). Nonces use counter mode to prevent reuse.
Security
- Encryption: AES-256-GCM (hardware-accelerated) with XSalsa20-Poly1305 fallback
- Key derivation: Random 256-bit key per transfer
- Nonce handling: Counter-mode nonces (no reuse)
- Authentication: Session ID verification prevents unauthorized connections
- Integrity: GCM/Poly1305 MAC on each chunk
Dependencies
- libsodium - Encryption
- zstd - Compression
- libarchive - Tar archive creation/extraction
- KCP (vendored) - Reliable UDP transport
- CLI11 - Command-line parsing
License
MIT