Rust Patcher
Secure fully decentralized software updates.
Implementation Flow
1. Add dependency
# Cargo.toml
[]
= "0.2"
= { = "1", = ["rt-multi-thread","macros"] }
2. Embed owner public key and start the updater
async
3. Generate signing key (one-time)
Output includes:
- Owner signing key saved to ./owner_key (z-base-32 encoded)
- Owner public key (z-base-32)
- Attribute snippet to paste into main: #[rustpatcher::public_key("")]
4. Build and sign releases
# build your binary
# sign the compiled binary in-place
5. Publish updates
- Run the newly signed binary on at least one node until a couple of peers have updated themselfs.
- The running process periodically publishes the latest PatchInfo to the DHT.
- Clients discover new PatchInfo, fetch the patch from peers, verify, and self-replace.
Run Example: simple
# Run signed app:
# if you increase the version in /crates/rustpatcher/Cargo.toml
# and build+sign+start another node, then the first
# node will update via the second node.
Network Architecture
sequenceDiagram
participant Owner as Owner Node (new version)
participant DTT as DHT Topic Tracker
participant Peer as Peer Node (old)
Owner->>DTT: Publish PatchInfo(version, size, hash, sig)
Peer->>DTT: Query latest PatchInfo (minute slots)
DTT-->>Peer: Return newest records
Peer->>Owner: Iroh connect (ALPN /rustpatcher/<owner>/v0)
Peer->>Owner: Auth = sha512(pubkey || unix_minute)
Owner-->>Peer: OK + Patch (postcard)
Peer->>Peer: Verify(hash, size, ed25519(pubkey))
Peer->>Peer: Atomic replace + optional execv restart
- Discovery: distributed-topic-tracker minute-slotted records over the DHT
- Transport: iroh QUIC, ALPN namespaced per owner key
- Authentication: rotating hash auth per minute bucket
Key Processes
- Version propagation
- Running a node publishes a PatchInfo record roughly every minute.
- Records are minute scoped with short TTL to avoid staleness.
- Peers scan current and previous minute for latest version.
- Patch fetch + verification
- Peer connects to other peers with newer version via iroh using an ALPN derived from the owner pubkey.
- Auth: sha512(owner_pub_key || unix_minute(t)) for t ∈ {-1..1}.
- Owner sends the signed patch (postcard-encoded).
- Self-update mechanism
- Write to temp file
- Atomic self-replace
- Optional immediate restart via execv (UpdaterMode::Now) or deferred (OnRestart / At(hh, mm))
Data Embedded in the Binary
- Fixed-size embedded region in a dedicated link section (.embedded_signature)
- Layout:
- 28 bytes: bounds start marker
- 32 bytes: binary hash (sha512 truncated to 32)
- 8 bytes: binary size (LE)
- 64 bytes: ed25519 signature
- 16 bytes: ASCII version (padded)
- 28 bytes: bounds end marker
At runtime, the library:
- Locates the embedded region
- Parses version/hash/size/signature
- Verifies the binary contents against the signed metadata
CLI Reference (rustpatcher)
- gen
- Generates a new ed25519 signing key in z-base-32; prints the public key and attribute snippet.
- sign --key-file
- Reads the compiled binary, computes PatchInfo, and writes it into the embedded region.
DO NOT COMMIT YOUR PRIVATE KEY!
# add this to your .gitignore
Notes:
- Keys are z-base-32 encoded on disk, the public key is embedded in code via #[rustpatcher::public_key("...")].
- Signing must be re-run after each new build that is intendet to self update.
- For every build target a seperate keypair is required (we don't want the arm users patching in x86 binaries).
Library API (overview)
- #[rustpatcher::public_key("")]
- Embeds the owner public key and the package version for verification
- rustpatcher::spawn(mode: UpdaterMode) -> Future<Result<()>>
- Starts discovery, publishing, distribution server, and updater
- UpdaterMode::{Now, OnRestart, At(h, m)}
How It Changed (vs previous rustpatcher)
- Single embedded region with explicit bounds, constant size, and zero-allocation compile-time construction
- Signature scheme clarified and minimal:
- sha512(data_no_embed) -> first 32 bytes as hash
- sign sha512(version || hash || size_le) with ed25519
- Owner key embedding via attribute macro, version captured from CARGO_PKG_VERSION and embedded as fixed-length ASCII
- Minute-slotted record publishing and discovery via distributed-topic-tracker
- iroh-based distributor with rotating minute auth derived from owner public key
- Simple updater modes: Now, OnRestart, At(hh:mm)
- CLI split: cargo install rustpatcher to manage keys and sign releases
Example
use UpdaterMode;
async
Release Workflow
- Generate key (once):
- rustpatcher gen ./owner_key
- Build + sign each release:
- cargo build --release
- rustpatcher sign target/release/ --key-file=./owner_key
- Deploy and run the signed binary on at least one node:
- It will publish PatchInfo and serve patches to peers.
- No need to have any exposed ports.
:
: