Psync — Lazy Project Archiver (pwr)
A Rust tool that archives projects from a laptop to a NAS and restores them on demand. Uses a custom TLS-encrypted protocol instead of rsync, with client-side age encryption so the server never sees plaintext data.
Architecture
pwr-core/ — Shared library: metadata, protocol, crypto, archive pipeline
pwr-server/ — NAS daemon: TLS listener, auth, project storage
pwr-cli/ — Client binary: CLI commands, TUI, shell integration
┌─────────────────┐ ┌──────────────────────┐
│ Laptop │ TLS │ NAS (Debian 12) │
│ │◄───────►│ │
│ pwr (CLI+TUI) │ :9742 │ pwr-server (daemon) │
└─────────────────┘ └──────────────────────┘
Security
- Transport: TLS 1.3 with certificate pinning
- Authentication: PSK-based HMAC-SHA256 challenge-response
- At-rest: age (X25519) client-side encryption — server stores only ciphertext
- Integrity: SHA-256 hash verification on every transfer
Quick Start
Server (NAS — Debian 12)
# Build
# Initialize (generates TLS cert, PSK)
# Start the daemon
Client (Laptop — Arch Linux)
# Build
# Configure (use the PSK printed by server init)
# Set up shell integration
# Archive a project
# Restore — just cd into it
# Or explicitly
# See status
# Launch TUI
Project File Format
Each tracked project has a .project.toml in its directory:
= 1
= "550e8400-e29b-41d4-a716-446655440000"
= "myproject"
= "/home/jacob/Projects/myproject"
= "nas.local:9742:/srv/pwr/projects/myproject"
= 14531252221
= 342
= "2026-07-04T18:23:12Z"
= "archived"
= true
= "age1qx0..."
After archiving, the directory contains only .project.toml — the
placeholder that triggers automatic restore when you cd into it.
Commands
| Command | Description |
|---|---|
pwr init |
Create client config (~/.config/pwr/config.toml) |
pwr archive <path> |
Encrypt and upload project, leave placeholder |
pwr restore <path> |
Download, decrypt, and extract project |
pwr ensure <path> |
Ensure project is local (for shell cd wrapper) |
pwr status |
Table of all tracked projects |
pwr list |
List projects with paths |
pwr log |
Transaction history |
pwr shell <sh> |
Generate shell integration (bash/zsh/fish) |
pwr tui |
Launch terminal UI (requires --features tui) |
Configuration
Client (~/.config/pwr/config.toml)
= 2
= "nas.local"
= 9742
= "a1b2c3d4..."
= true
= "sha256:..."
= "/home/jacob/Projects"
= 10
= 300
Server (/etc/pwr/server.toml)
= 1
= "[::]"
= 9742
= "/srv/pwr/projects"
= 500
= "/etc/pwr/server.crt"
= "/etc/pwr/server.key"
= "a1b2c3d4..."
= 32
= 300
Building from Source
Requirements: Rust 1.96+, OpenSSL/LibreSSL development headers (for ring)
Compatibility
| Platform | Status |
|---|---|
| Linux (x86_64, aarch64) | ✅ Full support — server, client, TUI, systemd service |
| macOS | ❌ Not supported — uses Linux-specific APIs (libc, systemd) |
| Windows | ❌ Not supported — uses Unix domain sockets and POSIX APIs |
The server and client both use:
libcfor daemonization, signal handling, and raw socket options- systemd for service management (
--with-service) - Unix filesystem permissions (
chmod 0o600for private keys)
These are deeply embedded and not abstracted behind platform gates. A macOS client-only port (without the TUI or daemon features) may be feasible; a Windows port is unlikely.
Both IPv4 and IPv6 are supported. The server binds dual-stack by default
([::]) and falls back to 0.0.0.0 if IPv6 is unavailable. The client
tries all resolved addresses (v6 first, then v4).
License
MIT