A peer-to-peer DDNS daemon for unstable or segmented networks.
p2p-ddns helps a group of machines learn and keep track of each other's current addresses when:
- hosts frequently change IPs because of DHCP
- the network is split across multiple broadcast domains
- you do not want to depend on a central coordination service
p2p-ddns maintains a shared view of node names and current addresses, then can project that view
into the local machine's hosts file. In practice, it gives you an application-level DDNS path:
- nodes announce a name and join a P2P network
- the network synchronizes current addresses
- each daemon can write resolved records into
/etc/hosts - local tools such as
ssh,ping, andcurlcan then resolve those names normally
- Not a proxy service
- Not a NAT traversal product by itself
- Not a full DNS server today
- Not a recursive resolver
- Not a replacement for an authoritative DNS service
The current DDNS integration path is hosts-file synchronization, not a standalone DNS protocol server.
- P2P node discovery and record synchronization over QUIC
- Offline-first operation on LANs via mDNS and static bootstrap tickets
- Optional relay / DHT support for harder network topologies
- Optional hosts-file synchronization for local name resolution
- Embedded persistence with
redb - Local management over Unix socket, plus optional HTTP admin endpoint
- Pure Rust implementation for cross-platform deployment
daemonmode runs the P2P node plus management serversclientmode talks to a running daemon over the local admin APITicketvalues act as bootstrap credentials for joining an existing network- Node state is synchronized with
iroh-gossipbroadcasts plus direct point-to-point messages - DDNS is implemented at the application layer by translating synchronized node records into hosts file entries
cargo buildGitHub Releases publish the following artifacts automatically when a new tag such as v0.2.0 is
pushed:
- Linux
x86_64andaarch64.tar.gzarchives - Linux
x86_64andaarch64.debpackages - macOS
x86_64andaarch64.tar.gzarchives - Windows
x86_64.ziparchives SHA256SUMS.txtfor artifact verification
p2p-ddnsctl can connect either through the local socket or through the daemon's HTTP admin
endpoint. Local loopback HTTP listeners allow ticketless management; non-loopback HTTP listeners
require a valid ticket.
Installed binaries:
p2p-ddns: daemon/node processp2p-ddnsctl: local management client
cargo run --bin p2p-ddns -- --primary --domain mynodeThe primary daemon prints Ticket (raw): <TICKET_STRING> on startup. Copy that value and use it
to join other daemons.
cargo run --bin p2p-ddns -- --ticket <TICKET_STRING> --domain mynodeBind to a specific IP while reusing the daemon's persisted port:
cargo run --bin p2p-ddns -- --primary --domain mynode --bind 192.168.1.10Bind to a specific IP and port:
cargo run --bin p2p-ddns -- --primary --domain mynode --bind 192.168.1.10:7777Bind using the first IPv4/IPv6 address found on a named network interface:
cargo run --bin p2p-ddns -- --primary --domain mynode --bind-interface eth0Notes:
--bindaccepts eitherIPorIP:PORT--bind-interfaceselects addresses from a named interface and reuses the persisted bind port--bindand--bind-interfaceare mutually exclusive- If no bind port has been persisted yet, one is generated and stored automatically
- Interface matching accepts the OS interface name, and on Windows also accepts the friendly name
client mode talks to the local daemon over a Unix socket and does not require a ticket by
default. A ticket is still required when adding other daemons to the network or when using the
optional HTTP admin endpoint.
cargo run --bin p2p-ddnsctl -- status
cargo run --bin p2p-ddnsctl -- list
cargo run --bin p2p-ddnsctl -- get-ticketTo talk to the daemon over HTTP instead of the local socket:
cargo run --bin p2p-ddnsctl -- --admin-http 127.0.0.1:8080 statusLoopback HTTP listeners allow ticketless local management. Non-loopback listeners require a valid ticket:
cargo run --bin p2p-ddnsctl -- --admin-http 192.168.1.10:8080 --ticket <TICKET_STRING> statusTo write synchronized records into the system hosts file:
sudo cargo run --bin p2p-ddns -- --primary --domain mynode --hosts-syncTo also publish a suffix form such as mynode.p2p:
sudo cargo run --bin p2p-ddns -- --ticket <TICKET_STRING> --domain mynode --hosts-sync --hosts-suffix p2pTo test safely without touching /etc/hosts, point hosts sync at a temporary file:
cargo run --bin p2p-ddns -- --primary --domain mynode --hosts-sync --hosts-path /tmp/p2p-ddns.hosts--bind <ADDR>: bind the P2P endpoint to a specific IP orIP:PORT--bind-interface <INTERFACE>: bind using the first IPv4/IPv6 address found on a named interface--config <DIR>: set the storage directory--no-mdns: disable local-network discovery--dht: enable PKARR/DHT discovery when built with--features pkarr-dht--admin-http <ADDR>: expose the admin API over HTTP--hosts-sync: write synchronized node records into a hosts file section--hosts-path <FILE>: override the hosts file path used by--hosts-sync--hosts-suffix <SUFFIX>: also write suffixed names such asnode.<SUFFIX>--relay-mode <disabled|default|staging>: configure relay usage--relay-url <URL>: use one or more self-hosted relays--reset-storage: intentionally wipe persisted node/topic/ticket state before startup
- Start a primary daemon.
- Copy the
Ticket (raw)value printed by the primary daemon at startup. - Start other daemons with that ticket.
- Use
listorstatusfromclientmode to inspect the network. - Use
get-ticketlater if you want to re-save or re-share the current network ticket.
- Start a primary daemon with
--hosts-sync. - Copy the
Ticket (raw)value printed by the primary daemon at startup. - Start other daemons with the same ticket and
--hosts-sync. - Wait for membership to converge.
- Resolve peer names locally through the managed
p2p-ddnssection in the hosts file.
--hosts-syncwrites only thep2p-ddnsmanaged section and leaves the rest of the hosts file untouched.- Writes require permission to modify the target hosts file. On Unix that usually means root, or a system service user with sufficient privileges.
- Invalid hostnames are skipped rather than written.
- Client-only admin sessions are not written into hosts records.
- The current design chooses one best IP per node for the local machine rather than writing every observed address.
The repository includes Debian/systemd packaging assets under
packaging/systemd.
The common deployment path is:
- Build or package the binary.
- Install the systemd service and wrapper.
- Edit
/etc/default/p2p-ddns. - Set
P2P_DDNS_ROLE=primaryordaemon. - Optionally enable hosts sync with:
P2P_DDNS_HOSTS_SYNC=1P2P_DDNS_HOSTS_SUFFIX=p2pFor bind-related settings:P2P_DDNS_BIND_ADDRESS=192.168.1.10orP2P_DDNS_EXTRA_ARGS="--bind-interface eth0" - Start the service with
systemctl enable --now p2p-ddns.
Run the default test suite:
cargo testDocker-backed topology tests are available separately and auto-skip when Docker is unavailable:
cargo test --test docker_p2p -- --nocaptureSee tests/integration/README.md for the Docker topology matrix.
- Runtime:
tokio - Networking:
iroh,iroh-gossip - Storage:
redb - Serialization:
postcard - CLI:
clap
This project is licensed under the MIT License. See LICENCE.