e701986a399f3d759d9f0e7cc6d70cc2c4bb8f19
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e701986a39 |
Add ephemeral pairwise Amnezia transport and runtime fallback
Plain WG and QUIC can be blocked or severely degraded while the iroh control connection still works. Add an awg carrier with one independent UDP socket, WireGuard engine and randomized masking profile per network/peer attempt. The smaller endpoint id negotiates fresh outer keys, PSK, header protection, header ranges, padding and signature templates through private authenticated control messages. Restart or fallback reentry generates a new profile; ordinary rekeying preserves the current profile. No pair parameters enter signed state, capabilities, persistent storage or public message events. Private payloads and serialized control buffers redact and zeroize themselves. Adapt the bounded packet masking engine from RunaRoo/AmneziaWG-RS at commit 51579a7d6e8882d1d8d6d00822f09e18a9423b73, retaining its Apache license and provenance. Declare WTFPL AND Apache-2.0 in package metadata to cover both new glue and the adapted source. Reuse boringtun instead of upstream's Node/global executors and interface machinery. This is Tsunagi AWG wire version 1, with standard WG headers restored before authentication; external Amnezia server compatibility is not implemented. DNS/SIP signature generators vary fields instead of replaying captures. Each attempt closes independently, bounds allocation, queues, candidates and fragment assemblies, and supports the full 64 KiB carrier contract using UDP packets at most 1200 bytes. Yield between fragment batches so large messages do not starve local socket readers. Keep carrier encryption separate from end-to-end tunnels so multihop relays still forward opaque ciphertext. The system owns the sole interface, signed addresses and admission. Plugins receive neither network secrets nor paths. Expose a bounded opaque control sender at the transport boundary; core control messages remain outside protocol crates. Optional STUN on the pair's actual socket adds IPv4 public candidates in CLI direct/relay mode, with a finite query deadline and an off/alternate-server option. Local mode remains offline. Symmetric NAT, IPv6-only paths and arbitrary UDP blocking can still require fallback; no AWG-over-QUIC or hidden AWG relay is introduced. Default carrier order is wg, awg, wg-quic, tcp-tls. Advance the fallback cycle after setup failures and sustained runtime loss, even after earlier cooldowns expire. Authenticated delivery probes traverse the actual encrypted tunnel, fit the interface MTU and never enter the TUN. Retire after a 20-second warmup and at least 60% loss over at least 15 completed recent observations. Preserve logical tunnels and control sessions while explicitly closing physical links, including TCP reader/writer tasks. Retain the newest private probe settings across independently queued control negotiation and inbound installation. CLI/tray reports keep end-to-end protocol and actual carrier separate. Control ALPN/version becomes tsunagi/ctrl/6 for the new private message; upgrade all members together. Data ALPN/envelopes remain version 5. Network identity derivation, transcript encoding, device identities, signed records, saved network configuration and release versions are unchanged. Validation on Linux through nix develop: - cargo fmt --all -- --check - cargo clippy --locked --workspace --all-targets -- -D warnings - cargo test --locked --workspace --all-targets, with the inherited TSUNAGI_CONTROL_SOCKET override removed: 504 passed, 2 pre-existing ignored Tests use real iroh membership, SQLite, UDP sockets and WireGuard encryption with memory TUNs. Cover three independent pairs, full-size IP and carrier frames, restart freshness/socket release, 80% carrier datagram loss, fallback to real iroh, fresh retries with working delivery checks, unequal small MTUs, malformed negotiations/profiles, role and resource bounds, probe forgery and STUN parsing. Earlier compile/lint iterations and focused tests caught RNG API/initializer mistakes, a retry assertion racing path installation, and a large-fragment burst overflowing loopback buffers; corrected before these successful checks. Normalized upstream license line endings for diff checks. Public NAT/STUN connectivity, actual DPI effectiveness, WAN performance, per-pair memory measurement, macOS/Windows, privileged TUN operations and manual tray interaction were not exercised. |
||
|
|
9966b50407 |
Rename the CLI package to tsunagi and its executable to tsng
The command line package is now `tsunagi` and builds the `tsng` executable, which the packages, the unit, the installers and the workflows install and run. The core library package becomes `tsunagi-core`, keeping the library name `tsunagi`, so no code changes `use tsunagi::...`. Directories follow (crates/tsunagi-core, crates/tsunagi). Every command shown to a person is now `tsng ...`. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
c724981bfd |
Added per-network LAN broadcast relay
LAN game discovery previously dropped IPv4 broadcasts at TUN ingress. Carry limited and subnet-directed UDP broadcasts to authenticated, opted-in members of the source network, including destinations reached through mesh relays. Preserve the original IP/UDP bytes and deliver received broadcasts only to the local TUN; never reflood them or expose another pair's plaintext at transit. Build immutable recipient snapshots on address and participation changes. The origin sends one ordinary end-to-end encrypted copy per recipient; the existing fast, bounded-hop transport router remains unchanged. Validate UDP framing, source ownership and destination admission without game-specific port rules. Keep network domains isolated and refuse implicit gateways to physical LANs. The separate broadcast policy/domain layer is the extension point for future authorized subnet exports; physical capture, bridging and LAN deduplication are deliberately not implemented yet. Persist default-on participation independently for each local network. Add join --no-broadcast/--broadcast and network broadcast <id> [on|off], including live updates and authenticated announcements. Joining without a flag preserves the saved choice. Opt-out stops local origination and delivery, while opaque unicast transit for other members keeps working. Migrate SQLite schema 3 to 4 without replacing identities or signed state. Use control ALPN 3 and local IPC protocol 14 for the new announcement/request shapes; update peers and restart running agents together. The data ALPN 4 envelope remains unchanged. No release version bump, tag or push is included. Document agent-owned commits in AGENTS.md: short English subjects, explanatory bodies, scoped staging, honest validation, and repository-local fallback author AB <ab@hexor.cy> only when an effective name/email is missing. Release actions remain the user's responsibility. Validation on Windows: cargo fmt --all -- --check; cargo check --locked --workspace --all-targets; cargo clippy --locked --workspace --all-targets -- -D warnings; release workspace/all-target tests: 313 passed. The two existing SQLite wipe failures (a_wipe_removes_everything_and_the_next_start_is_a_stranger and wiping_twice_is_as_ordinary_as_wiping_once) were explicitly skipped; the public-DHT smoke test and forwarding benchmark remain ignored by default. New coverage exercises real iroh/WireGuard multihop fanout, single delivery, runtime opt-out, unicast replies, domain isolation, malformed input and schema migration. TUNs are in-memory; actual games and OS adapter selection were not tested. |
||
|
|
b4f3e57c8d |
Implement fast userspace multihop mesh routing
Replace the one-intermediate-peer relay with protocol-scoped connectivity graphs and precomputed shortest-path/ECMP forwarding snapshots. Independent transport readers forward opaque transit frames without a plugin or TUN round trip. Carry source, destination, a bounded hop limit and stable flow tags; preserve end-to-end WireGuard links across topology changes. Classify IP flows before encryption and preserve their tags through the WireGuard pending queue. Add offline four-agent path-change coverage, loop and isolation tests, and an opt-in release forwarding microbenchmark. Bump control/data ALPNs while preserving persistent network identities and state. Also include the pending Windows Mainline idle-timeout fix and its regression test, using a reproducible vendored dependency patch. Validation: fmt, workspace Clippy with warnings denied, and 307 release tests passed. Two pre-existing Windows SQLite wipe failures were excluded; public DHT and the manual benchmark remain ignored by default. The forwarding microbenchmark measured 103 ns (64 B) and 202 ns (1280 B) per transit packet, excluding encryption and socket I/O. |
||
|
|
990b9f2e0f |
Describe the layers as they now stand
The module table still had the plugin inside the core and no mention of the overlay or the DNS view, and the stale path in the testing notes pointed at a directory that had moved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
84c06c6cac |
Allocate IPv4 addresses and keep them, as signed state
Derived IPv4 addresses could not survive anything: they changed with the range, and there was no way for a member to come back to the one it had. Addresses are now allocated and recorded as signed facts, which is the first slice of the model in docs/sync-model.md. src/state/ holds one record per author per network, carrying that author's complete current statement, signed with its persistent device key over a length-prefixed canonical encoding. Merging follows the model's rules: a higher version wins, an older one never rolls back a newer, duplicates are idempotent, absence from a snapshot is not deletion, and a same-version conflict is resolved identically on every replica and reported rather than letting replicas diverge. Records are persisted in state.sqlite, with the record and the author's version counter committed in one transaction before anything is announced, and distributed as a State control message that is merged into what the receiver already holds. No vote, deliberately, despite the request. A majority is not a trust root here — anyone with the secret can mint identities — and a quorum would stall with one peer online and diverge across a partition. Signatures plus a deterministic merge converge without either failure mode: two members claiming one address at once are resolved by the lower endpoint id, and the loser allocates again with a higher version. The range moved from the plugin to the agent, defaults to 10.13.37.0/24, and is now agreed rather than configured per member: a joining agent adopts what the network already uses, so --ipv4-range only matters for whoever starts it. The announcement went back to identity only (version 3) since the range travels in signed records now. A release tombstone exists and merges correctly, but nothing emits one yet. 116 tests. The headline ones: an address survives restarting both agents, three members get three distinct addresses, and a member started with a different range adopts the one in use. Confirmed by hand with two CLI agents restarted end to end. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
21be7e9b44 |
Separate control and data logically, move WireGuard into userspace, add a CLI
Corrects the architecture on two points raised in review, while the project is still small enough to change cheaply. 1. Control and data are separated *logically*, not physically. The old reading — "nothing but control may ride on iroh" — threw away iroh's whole value and would have forced the data plane to reimplement STUN, ICE and a relay. Now both planes ride on iroh with different ALPNs and different connections, so the data plane inherits hole punching and relay fallback, while proto/ still knows nothing about packets and dataplane/ knows nothing about the control protocol. New boundary: PacketTransport / PacketLink, an authenticated unreliable datagram channel per (network, peer, protocol). tsunagi/data/1 runs the same membership handshake, then DataOpen/DataOpenAck, then QUIC datagrams. Only the smaller endpoint id dials, so exactly one link exists per pair. A plugin is handed links and never learns reachability, so the WireGuard announcement shrank to a public key: there is no address left to lie about. 2. WireGuard now runs in userspace, on boringtun's protocol state machine. No kernel module, no wg tool, no ip shell-out, no loopback proxy: the wgtool, backend and bridge modules are gone. Only creating a TUN device needs privileges, and that sits behind TunFactory, so the entire data plane — handshake, encryption, routing, address ownership — is tested with none. Address ownership is enforced rather than believed: outbound packets go to the owner of the destination address, inbound packets are dropped unless their source is the address derived for the peer that sent them. 3. A `tsunagi` binary: secret, doctor, id, up. It owns the runtime, the logging subscriber and Ctrl-C, which the library still refuses to. Also fixes a reference cycle where IrohTransport held Arc<Inner>, which kept the databases open and the directory lock held after shutdown; two storage tests caught it once the cycle existed. 81 tests pass offline with no privileges, including real IPv6 packets crossing a real WireGuard tunnel over real iroh connections. Verified by hand: two CLI processes forming a mesh both on loopback and via n0 discovery using only an endpoint id. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ea7aaa2b69 |
Implement the WireGuard data plane plugin
The first IP plugin, built on the data plane boundary the core already had. Plugin: - one X25519 key per network in the plugin's own wireguard.sqlite, separate from the iroh identity and from the network secret; a damaged store is an error, never a silently regenerated identity - deterministic IPv6 ULA overlay: every member derives the same /64 from the network id and its own /128 from its WireGuard public key, so no coordinator allocates addresses - AllowedIPs are derived locally, never taken from a peer's announcement, so a member cannot claim another member's overlay address; a mismatched claim is rejected - bounded, versioned, validated announcement carried as the existing opaque capability payload, which the core still never parses - each agent builds its own full-mesh configuration (N-1 peers) and reconciles on every change and on a timer, repairing drift - WireguardBackend abstraction: RecordingBackend in memory, and WgToolBackend driving real wg/ip on Linux, split into a pure planner plus parsers and a thin executor so everything interesting is testable without root Core, three generic additions the plugin needed: - IpPlugin::on_network_activated, so per-network state is ready before peers - PluginContext for re-announcements and error reports from plugin tasks, with errors counted by the owning network runtime - IpPlugin::shutdown, awaited with a grace period, so system objects go away 94 tests pass offline with no privileges: 35 new WireGuard unit tests and 12 integration tests over real iroh connections. The real wg/ip backend needs root and is behind --ignored in tests/wireguard_system.rs; it was not run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
7cea9afa37 |
Proof-of-concept mesh agent library over iroh
Working library with real iroh connections, not an interface sketch: - persistent device identity in state.sqlite, stable across restarts - deterministic network space derived from name + secret via HKDF-SHA256, with frozen labels and unambiguous length-prefixed encoding - replaceable discovery returning unverified candidates only; static bootstrap, in-memory test backend and a composite - real iroh connections plus an explicit mutual membership proof: HMAC-SHA256 over a role-separated transcript bound to the TLS exporter, the network id and both endpoint identities - small versioned control protocol: handshake, announcement, ping/pong - multiple networks per agent with enforced isolation - automatic reconnect with bounded backoff and jitter - mandatory state vs disposable cache, with a real directory ownership lock - status snapshots, event stream and honest diagnostics 47 integration and unit tests cover the required scenarios offline on loopback. Snapshots, revocations and WireGuard are designed for and documented, not implemented. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |