Provide named VIPs with independent protocol/port listeners and node/port backends so applications can use one DNS endpoint without a separate load balancer. Every authenticated member may create, edit or delete any service. Client agents select the eligible backend with the lowest measured encrypted data-plane RTT, including relayed paths, and pin existing flows. UDP recovers on another backend after failure; established TCP is never replayed or moved. An unanswered empty TCP SYN may retry another backend before establishment. Keep signed multi-writer field registers in state/vip.rs. Independent backend edits merge, same-field conflicts use Lamport/author/canonical ordering, and listener generations prevent old backend entries returning on port recreation. Permanent service tombstones protect replay while allowing obsolete fields to be compacted. Active-service and deletion-history limits are separate. Commit complete snapshots and the author's durable counter atomically before local routing changes or publication. Restore counters from newer genuine author updates learned from replicas after an old backup is restored. Implement bounded client-side address/port NAT at overlay ingress and reverse translation only after authenticated source/destination admission. Reserve OS source ports to avoid direct-flow collisions and quarantine retired ports for late replies. Handle a backend on the client itself, scoped fragment assembly, checksums, re-fragmentation and quoted ICMP/PMTU headers. Plugins and encrypted transit retain their existing contracts. Data-path probes measure RTT; bounded fair TCP connect checks are enabled by default and configurable per listener. Arbitrary UDP application readiness remains unknown; path checks and explicit port errors drive eligibility without protocol-specific probe payloads. Expose the shared draft API through local IPC, simple vip/listener/backend CLI commands, JSON, editor-based atomic updates and watch output. Add a VIP button on each network tile and a separate tray editor. Edits and removals stay local until Save; stale revisions retain the draft, and closing a dirty window offers explicit discard. Report local routing choices, health reasons, RTT age, active and draining bindings, IP counters, rates, drops and UDP failovers. Extend DNS with service names; keep numeric addresses secondary and preserve existing bindings while VIP addresses or listeners change. This changes control wire ALPN/version to tsunagi/ctrl/8 and local IPC to 22; all members and local clients must upgrade together. Data ALPN remains 5. SQLite schema 10 migrates existing mandatory state without changing identity, secrets, network ids, derivation labels, handshake encoding or node records. Crate versions and release tags are unchanged. No installed agent or tray was replaced. Commands, limits and operational semantics are in docs/vip.md. Checks on Linux in nix develop: - cargo fmt --all -- --check - cargo clippy --locked --workspace --all-targets -- -D warnings - cargo test --locked --workspace --all-targets: 559 passed, 3 ignored - git diff --cached --check The full suite includes real encrypted TCP/UDP VIP traffic, backend-stop UDP failover, real loopback TCP check recovery, source admission, signed merges, stale/deleted state, SQLite migration and rollback, restart/author recovery, network isolation, actual CLI/DNS processes, and headless text-entry/Save-button interaction. Discovery and memory TUNs are the existing test substitutions; authentication, transport, encryption and SQLite remain real. Initial checks outside Nix failed for missing GTK pkg-config metadata, and an initial CLI test inherited TSUNAGI_CONTROL_SOCKET. Final checks use the Nix environment with that variable unset so tests address their temporary sockets. Root/internet-dependent tests remain ignored. No live host TUN, native desktop session, macOS or Windows validation was performed.
122 lines
4.0 KiB
Rust
122 lines
4.0 KiB
Rust
//! Tsunagi: a proof-of-concept agent for small private mesh networks.
|
|
//!
|
|
//! See `README.md` for the exact scope of this proof of concept, and
|
|
//! `docs/architecture.md` / `docs/protocol.md` for the design.
|
|
//!
|
|
//! # Shape of the library
|
|
//!
|
|
//! * [`identity`] — persistent device identity and deterministic network space
|
|
//! identity.
|
|
//! * [`storage`] — mandatory state (`state.sqlite`) and separately disposable
|
|
//! cache (`cache.sqlite`).
|
|
//! * [`discovery`] — pluggable sources of *candidate* addresses. Candidates are
|
|
//! never trusted peers.
|
|
//! * [`proto`] — the control protocol: framing, messages, handshake.
|
|
//! * [`net`] — the iroh connectivity adapter and its observability surface.
|
|
//! * [`agent`] — the runtime: agent lifecycle, per-network runtimes, reconnect.
|
|
//! * [`dataplane`] — the contract IP plugins satisfy, the packet transport,
|
|
//! and the WireGuard data plane.
|
|
//! * [`state`] — signed records that outlive a session, and the rules for
|
|
//! merging them between replicas.
|
|
//! * [`ipc`] — the local control interface a command line tool talks to. An
|
|
//! adapter over the public API; the core does not know it exists.
|
|
//!
|
|
//! # What this library deliberately does not do
|
|
//!
|
|
//! It never starts a global tokio runtime, never installs a global tracing
|
|
//! subscriber, never handles process signals, never forks and never calls
|
|
//! `process::exit`. Several independent agents can run in one process.
|
|
//!
|
|
//! Only control messages travel over iroh. User IP traffic is not tunnelled
|
|
//! through it.
|
|
|
|
#![deny(rustdoc::broken_intra_doc_links)]
|
|
|
|
/// A boxed future, used where a trait must stay object safe.
|
|
pub type BoxFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + 'a>>;
|
|
|
|
pub mod agent;
|
|
pub mod config;
|
|
pub mod dataplane;
|
|
pub mod discovery;
|
|
pub mod dns;
|
|
pub mod error;
|
|
pub mod identity;
|
|
pub mod ipc;
|
|
pub mod net;
|
|
pub mod overlay;
|
|
pub mod proto;
|
|
pub mod state;
|
|
pub mod storage;
|
|
pub mod task;
|
|
#[cfg(feature = "testing")]
|
|
pub mod testing;
|
|
pub mod vip;
|
|
|
|
pub use agent::{Agent, AgentStatus, Event, NetworkStatus, PeerStatus};
|
|
pub use config::{AgentConfig, Limits, ReconnectPolicy, StoragePaths, TransportPolicy};
|
|
pub use error::{Error, ProtocolError, Result};
|
|
pub use identity::{
|
|
DeviceIdentity, DiscoveryKey, NetworkDescriptor, NetworkId, NetworkName, NetworkSecret,
|
|
};
|
|
|
|
/// Re-exported iroh types that appear in this crate's public API.
|
|
pub mod iroh_types {
|
|
pub use iroh::{EndpointAddr, EndpointId, RelayUrl};
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
pub mod test_support {
|
|
//! Internals exposed for this crate's own negative tests.
|
|
//!
|
|
//! **Not part of the stable API.** It exists so the integration tests can
|
|
//! hand-craft handshakes — a valid proof to replay on another connection, a
|
|
//! message sent before authentication, an oversized frame — which is the
|
|
//! only way to test those rejections against a real agent.
|
|
|
|
use iroh::endpoint::Connection;
|
|
|
|
use crate::error::ProtocolError;
|
|
use crate::identity::NetworkKeys;
|
|
use crate::proto::handshake;
|
|
|
|
/// Exposes the derived handshake authentication key.
|
|
pub fn auth_key(keys: &NetworkKeys) -> [u8; 32] {
|
|
*keys.auth_key()
|
|
}
|
|
|
|
/// Derives this connection's channel binding material.
|
|
pub fn channel_binding(
|
|
conn: &Connection,
|
|
network_id: &[u8; 32],
|
|
) -> Result<[u8; 32], ProtocolError> {
|
|
handshake::channel_binding_for_test(conn, network_id)
|
|
}
|
|
|
|
/// Computes a handshake proof for the given role.
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn compute_proof(
|
|
auth_key: &[u8; 32],
|
|
role: &str,
|
|
version: u16,
|
|
network_id: &[u8; 32],
|
|
initiator: &[u8; 32],
|
|
responder: &[u8; 32],
|
|
channel_binding: &[u8],
|
|
nonce_initiator: &[u8; 16],
|
|
nonce_responder: &[u8; 16],
|
|
) -> [u8; 32] {
|
|
handshake::proof_for_test(
|
|
auth_key,
|
|
role,
|
|
version,
|
|
network_id,
|
|
initiator,
|
|
responder,
|
|
channel_binding,
|
|
nonce_initiator,
|
|
nonce_responder,
|
|
)
|
|
}
|
|
}
|