feat: LAN-direct — Locator seam + QUIC + mDNS (C1 + C2)#42
Closed
frahlg wants to merge 8 commits into
Closed
Conversation
Approved 2026-06-13. CLI-only, Go-only. A Locator seam (in package client) turns a Machine into a live peer.MsgConn; Attach composes [LAN, Relay]. LAN uses QUIC (self-signed + skip-verify; real auth = Noise-KK + binding) and mDNS discovery. Relay/WAN path unchanged. QUIC-over-WAN (DCUtR) is a stated future locator, not built now. Stacked on the B1.4+B1.5 branch (reuses ownerPubFromBinding, the cached binding, and wallet owner_id). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a Locator interface (package client) that turns a Machine into a live peer.MsgConn; Attach composes [relayLocator] and runs Noise-KK over whatever connects. Today's relay path moves verbatim into relayLocator. Attach now returns peer.MsgConn (was *peer.DataChannel; consumers use only Send/Recv). Behavior-preserving: the relay e2e tests stay green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A length-framed QUIC bidi stream as a peer.MsgConn, shared by client (LAN dial) and agent (LAN listen). QUIC TLS is dumb transport: the real auth is Noise-KK + the wallet binding that runs inside, so ClientTLS skips verification. ServerTLS uses an ephemeral self-signed cert. ALPN miranda/lan/v1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A lanLocator that resolves a machine_id to a LAN address (mDNS in prod, injectable resolver in tests), QUIC-dials it, and sends the wallet binding as frame 0 before Noise-KK. Returns ErrUnreachable on miss / no-wallet so Attach falls through to the relay. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…accept mir up can accept LAN-direct QUIC connections (advertised via mDNS as _miranda._udp). The wallet binding arrives as frame 0; the agent checks IsOwnerPinned + verifies the binding (reusing ownerPubFromBinding) and pins binding.x25519, then runs the SAME authenticated PTY session as the relay path via the extracted serveAuthenticated helper. admit() bounds pre-auth handshakes. Not yet auto-started (wiring + --no-lan is next). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-only mir attach now tries LAN-direct (mDNS+QUIC) first with a ~600ms budget, then falls back to the relay; --relay-only skips LAN. mir up starts the QUIC listener + mDNS advertisement (in Up, after the paired-owner guard) unless --no-lan; LAN start failure is non-fatal — the relay path always serves. attachLocators() makes the LAN-first-vs-relay-only decision unit- testable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lve test SECURITY.md gains an honest LAN-direct residual-exposure note (new inbound listener surface + mDNS leak; same Noise-KK + binding trust, --no-lan to disable). README documents LAN-direct + the flags. A skippable live-mDNS test validates the prod resolver where multicast is available. Spec notes happy-eyeballs as the future latency refinement and netsim as a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Replace the sequential LAN-then-relay dial with dialStaggered: the LAN locator starts immediately, the relay only after a ~200ms head start; the first live MsgConn wins, the loser is cancelled and any late-connecting loser is cleaned up. On the LAN, LAN-direct wins inside the head start so the relay is never contacted (a successful LAN attach stays relay-free). Remote attaches pay only the head start, not the full ~600ms LAN budget. Staggered (not a naive simultaneous race) on purpose: it keeps the relay-free property that is the point of LAN-direct. Race-tested (-race). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
docs/superpowers/specs/2026-06-13-c1-c2-lan-direct-locator-design.md(approved 2026-06-13). CLI-only, Go-only — the browser keeps using the relay; no web
changes, no byte-identical crypto gate touched.
What this delivers
mir attachreaches amir upnode on the same LAN with no relay — zero-config mDNSdiscovery + a direct QUIC transport — by inserting a
Locatorseam under the unchangedNoise-KK session. The relay/WAN path is untouched.
How it works
Locator(packageclient) turns aMachineinto a live
peer.MsgConn;Attachcomposes[LAN, relay]and runs Noise-KK over thefirst that connects. Today's relay path moves verbatim into
relayLocator. Relay e2estays green (behavior-preserving).
LANLocator).mir upadvertises_miranda._udp(instance =machine_id); the clientbrowses + matches. Discovery yields only an address — never trust.
is Noise-KK + the wallet binding that runs inside the stream (
internal/quicmsg, alength-framed
peer.MsgConn).IsOwnerPinned+VerifyBinding(reusingownerPubFromBinding), pinsbinding.x25519, then runs thesame authenticated PTY session as the relay path (
serveAuthenticated).mir attach --relay-onlyandmir up --no-lan.Trust (unchanged) + new surface
Locate but never impersonate: Noise-KK pins
host_pub, the agent pins the wallet. mDNSspoofing / a rogue LAN host yields at worst a failed handshake (DoS) — never
impersonation or plaintext. New exposure: the agent now accepts inbound LAN connections
(bounded by the same pre-auth limiter) and mDNS reveals an opaque
machine_id. Both offwith
--no-lan. Documented inSECURITY.md.QUIC-over-WAN is the destination, not this step
WAN's hard part is NAT traversal (ICE), which QUIC alone doesn't solve; WebRTC already does
and ships. A future
QUICHolePunchLocator(DCUtR + circuit-relay, north-star C4) drops intothe same seam — captured as a stated future, not built.
Tests
internal/quicmsg: length-framed round-trip (incl. 70 KB frame) + ctx-cancel.client:lanLocatorsends frame0 binding;ErrUnreachableon miss/no-wallet;attachLocatorscomposition;dialFirstfallthrough; skippable live-mDNS resolve.agent: real-shell echo over QUIC, no relay; unpinned binding rejected pre-Noise;relay e2e still green after the
serveAuthenticatedextraction.go test ./...,go vet,gofmtclean. Realmirbinary exposes--relay-only/--no-lan.New deps (non-crypto, Go-only)
github.com/quic-go/quic-gov0.60.0,github.com/grandcat/zeroconfv1.0.0.Follow-up (not in this PR)
deploy/netsimLAN path (cross-container mDNS); the Go tests already prove the wire.adding concurrency to the working relay path).
🤖 Generated with Claude Code