iroh links netsky across machines

Iroh gives netsky one cross-machine transport that still feels like the local bus: same JSON envelopes, same inbox lifecycle, different wire. Machine A dials machine B over QUIC with TLS 1.3, B checks the peer’s verified EndpointId against an allowlist, reads one bounded envelope, rewrites from to the verified label, and drops the file into the same delivery path the local sources already use. The point is not “distributed systems.” The point is two machines, one trust model. (src/crates/netsky-io/src/sources/iroh/mod.rs:1-16, src/crates/netsky-io/src/sources/iroh/mod.rs:379-444)

problem #

Cross-machine traffic only matters if it behaves like the local bus under stress. Two laptops still need authenticated transport, an allowlist, a bounded payload format, and a network path that survives ordinary NAT mess without teaching the application about relays or port choreography. Netsky keeps those constraints below the bus layer: iroh handles the QUIC endpoint and N0 network stack, the peer list comes from ~/.config/netsky-io/peers.toml plus env override, the wire format is one length-prefixed JSON envelope per uni-stream with a 1 MiB cap, and unknown peers are refused before payload read. (src/crates/netsky-io/src/sources/iroh/mod.rs:123-130, src/crates/netsky-io/src/sources/iroh/mod.rs:392-407, src/crates/netsky-io/src/sources/iroh/peers.rs:107-133, src/crates/netsky-channels/src/iroh/mod.rs:48-69)

shape #

The transport stays small on purpose. The ALPN is pinned to /netsky/agent/1. Identity is the endpoint key stored at ~/.config/netsky-io/iroh/<scope>.key, scoped to agent<N> inside the constellation or workbench in a direct shell. NETSKY_IROH_KEYFILE can point at a different keyfile, but it does not create delegated authority. It only changes which key the local process uses. (src/crates/netsky-channels/src/iroh/mod.rs:6-8, src/crates/netsky-io/src/sources/iroh/keyfile.rs:35-70, src/crates/netsky-io/src/sources/iroh/keyfile.rs:119-151)

+-------------------+        QUIC + TLS 1.3        +-------------------+
| machine A         | ---------------------------> | machine B         |
| netsky iroh send  |                              | iroh source       |
+-------------------+                              +-------------------+
         |                                                  |
         | one JSON envelope                                | remote_id() -> allowlist label
         |                                                  v
         |                                        ~/.netsky/channels/iroh/<label>/inbox/
         |                                                  |
         +-------------------------------------------------> shared delivery adapter

The payload’s from field is not trusted. Netsky overwrites it with the label bound to the verified EndpointId.

That replacement is the structural claim. handle_incoming completes the handshake, derives the remote id with remote_id(), resolves it with label_for, and only then reads the length-prefixed envelope. The peer decides the sender label. The payload does not. Accepted envelopes land in ~/.netsky/channels/iroh/<label>/inbox/, and the poll loop reuses the same inbox/, claimed/, delivered/, and poison/ lifecycle as the rest of netsky’s delivery stack. (src/crates/netsky-io/src/sources/iroh/mod.rs:383-444, src/crates/netsky-io/src/sources/envelope_delivery.rs:55-149)

sequenceDiagram
    participant A as machine A
    participant B as machine B
    participant L as peers allowlist
    participant I as iroh inbox

    A->>B: QUIC + TLS 1.3, ALPN /netsky/agent/1
    B->>L: remote_id, then label_for
    L-->>B: paired label or refuse
    B->>B: read 4-byte length, then JSON envelope
    B->>B: overwrite from field with verified label
    B->>I: write envelope file
    I-->>B: drain inbox, claimed, delivered

concrete interface #

The operator surface is netsky iroh, not a hidden MCP-only side channel. status prints the local EndpointId, key path, peers file, paired peers, and restart hints. pair add|list|remove manages the allowlist. send dials one label, writes one envelope, and closes. In practice there is a fourth command that matters just as much: whoami bootstraps the scoped key on first run and prints the local EndpointId so another machine can pair against it. (src/crates/netsky-cli/src/cmd/iroh.rs:4-42, src/crates/netsky-io/src/sources/iroh/mod.rs:87-92, src/crates/netsky-io/src/sources/iroh/mod.rs:237-312)

netsky iroh whoami
netsky iroh pair add work <work-endpoint-id>
netsky iroh status --json
netsky iroh send work "check branch session5-blog-iroh-rewrite and reply with the sha"

usage #

The old env-based pairing path still exists. NETSKY_IROH_PEER_<LABEL>_NODEID loads after TOML and wins for the same label, which makes rollback and one-off override cheap. The durable path is already the CLI. Run netsky iroh whoami on each machine, store the other machine with netsky iroh pair add <label> <node-id>, restart the iroh source so it reloads peers, then send a one-shot test. pair add writes ~/.config/netsky-io/peers.toml atomically with owner-only permissions, and status tells you when the source is not running or still depending on env fallback. (src/crates/netsky-io/src/sources/iroh/peers.rs:7-13, src/crates/netsky-io/src/sources/iroh/peers.rs:323-365, src/crates/netsky-io/src/sources/iroh/mod.rs:262-312)

observability #

~/.netsky/meta.db has a dedicated iroh_events table with bounded event types: connect, evict, reconnect, and handshake_refused. The writer stores peer_id_hash, not the raw NodeId, so the system can count churn without leaking endpoint identities into reports. Current call sites record connect when an allowlisted peer is accepted and handshake_refused when an unknown peer is dropped before payload read. Eviction rides a different path today: a tracing layer watches for iroh’s duplicate-endpoint warning and fires one netsky escalate per process so the owner sees the collision quickly. (src/crates/netsky-db/README.md:27-45, src/crates/netsky-io/src/observability.rs:146-172, src/crates/netsky-io/src/sources/iroh/mod.rs:392-415, src/crates/netsky-io/src/sources/iroh/eviction.rs:1-39)

what this is not #

This is not multicast. One label resolves to one EndpointId, and netsky iroh send dials one target at a time. This is not a new store-and-forward system. The transport writes into the inbox lifecycle netsky already has. Replay comes from inbox/, claimed/, delivered/, and poison/, not from a second cross-machine queue. This is not identity delegation. The identity is the scoped keyfile on disk. Share it between live processes and the relay will evict one. Rotate it and you re-pair peers. (src/crates/netsky-io/src/sources/iroh/peers.rs:175-199, src/crates/netsky-io/src/sources/iroh/mod.rs:466-516, src/crates/netsky-io/src/sources/envelope_delivery.rs:55-149, src/crates/netsky-io/src/sources/iroh/keyfile.rs:64-70)

That narrow scope is the feature. Iroh carries verified bus envelopes across machines, then gets out of the way.