Online Transport MVP
Status: Self-reviewed draft
Scope: online cross-network synchronization between authorized paired devices
Out of MVP: mailbox, durable object storage, public gossipsub, public proof-of-work services
Purpose
Section titled “Purpose”The online transport MVP connects paired devices across local and non-local networks and carries the application sync protocol between them.
It does not define vault merge semantics. It does not make libp2p behavior part of the application test surface. It provides authenticated, bounded streams between devices so the v2 revision protocol can exchange heads, requests, records, and resources.
Design Rule
Section titled “Design Rule”The networking layer is responsible for reachability and stream security. The application layer is responsible for vault authorization and sync correctness.
libp2p Peer ID proves transport identity.vault membership proves application authority.A known Peer ID is not sufficient to fetch vault data unless the connected peer also proves membership in the vault sync group.
MVP Connectivity Order
Section titled “MVP Connectivity Order”The dialer should try the least stateful and least centralized options first:
1. Local mDNS.2. Cached addresses from prior successful sessions.3. Scoped rendezvous namespace for paired devices.4. Direct QUIC/TCP dial.5. AutoNAT reachability assessment.6. Circuit Relay v2 fallback when direct dialing fails.7. DCUtR upgrade over the relayed connection when both peers have candidates.The MVP should not publish vault heads or provider records to public DHT or public gossipsub topics by default.
The current implementation completes steps 2 through 7. Step 2 uses a versioned, bounded local cache. Addresses enter the cache only after a membership-authorized vault sync succeeds and the candidate address itself establishes the expected authenticated libp2p peer identity. They are retried after restart and expire after 30 days. Discovery alone never makes an address durable. Each entry retains its authenticated vault and remote-device bindings; current configured memberships prune revoked bindings, while locked-only bindings remain dormant until unlock. Corrupt cache contents are replaced by one bounded redacted diagnostic marker before a later authenticated success rebuilds the cache. Address expiry is tracked per address, so a successful path cannot indefinitely refresh stale alternatives.
When rendezvous nodes are configured, each active unlocked vault derives a
64-character namespace from its current repo_secret, membership epoch, and a
dedicated rendezvous context. The daemon registers configured direct external
addresses and active relay reservations, refreshes registrations and incremental
discovery, and replaces the namespace set immediately after membership changes
or unlock. Old-epoch and
locked-vault namespaces are not retained. mDNS, cached, and rendezvous address
sets remain independent so expiry or reset of one source cannot remove a path
learned from another. The namespace and vault metadata are excluded from logs
and status output.
Discovery also performs periodic full revalidation. Locally learned addresses use a lease shorter than the server registration TTL because the rendezvous protocol reports the original TTL rather than the registration’s remaining age. Expiry is tracked by rendezvous node, namespace, and peer so one expired registration cannot remove an address still supported by another live source.
Namespace removal sends an unregister request immediately or after the rendezvous node reconnects. If the node remains unavailable, its existing opaque registration can remain visible to a party that knows the old namespace until the server’s bounded registration TTL expires. Application membership authorization still rejects removed devices throughout that interval.
Direct TCP with Noise/Yamux and direct QUIC are implemented as first-class listeners and dial transports. Runtime readiness waits for both configured listeners, and loopback integration tests exchange the authenticated sync protocol over each transport. Identify exchanges signed peer records without vault metadata and supplies ephemeral observed-address candidates. AutoNAT v2 tests those candidates through connected supporting infrastructure and publishes fresh advisory reachability state without blocking daemon readiness. Candidate evidence expires after ten minutes, returns to unknown while an independent dial-back is retried, and is withdrawn from rendezvous if revalidation fails or cannot complete within the bounded grace period.
Configured relay nodes are optional and retried after reservation loss. Active relay reservation addresses are registered through the same epoch-scoped rendezvous namespace. Dialing prefers every direct path before a circuit address. A relayed connection activates DCUtR; success pins new application requests to the direct authenticated connection while retaining the relay as fallback, without interrupting in-flight relay streams. If the preferred direct connection later closes, subsequent requests resume over the surviving relay. Failure keeps application sync on the relay circuit.
Application Protocols
Section titled “Application Protocols”The transport carries versioned application protocols. Initial protocol names:
/lattice/vaultsync/heads/2.0.0/lattice/vaultsync/fetch/2.0.0/lattice/vaultsync/membership/1.0.0Mailbox and storage protocols are reserved for later documents and should not be required for online MVP success.
The current pre-production implementation multiplexes this exchange over
/lattice/sync/7 with application protocol version 11 and repo-alias context
online-v8. This is a hard cut: the stream decodes protocol 11 only, so all
remaining devices must upgrade before they can authenticate or receive a
post-removal membership transition.
Heads Protocol
Section titled “Heads Protocol”Purpose:
exchange signed known heads for a repo aliasRequired operations:
- list local signed heads for a repo alias
- send signed head summaries
- request missing manifest or descriptor metadata by revision ID
The heads protocol must return enough signed data for the receiver to decide whether it needs to fetch anything. Unsigned server or transport metadata is advisory only.
Fetch Protocol
Section titled “Fetch Protocol”Purpose:
fetch v2 records, resource descriptors, and encrypted resource chunks from an authorized peerRequired operations:
- fetch record descriptors by ID and version
- fetch resource descriptors by hash
- fetch encrypted resource chunks by hash
- report unavailable objects without treating absence as deletion
The fetch protocol must reject requests before serving data unless the remote device proves current membership.
Membership Protocol
Section titled “Membership Protocol”Purpose:
exchange membership documents and prove device authorizationRequired operations:
- get current membership document for a repo alias
- prove possession of the listed device signing key
- bind the live libp2p Peer ID to the member device for this session
- announce a newer membership epoch
Phase-1 Minimum Membership Contract
Section titled “Phase-1 Minimum Membership Contract”Phase 1 may implement only the minimum membership behavior needed for safe online sessions, but it still needs a signed membership artifact.
The minimum artifact is:
{ "type": "lattice.membership.v1", "repo_commitment": "...", "epoch": 1, "members": [ { "device_id": "...", "device_signing_key": "...", "device_encryption_key": "...", "role": "writer" } ], "removed": [], "policy": { "membership_authority": "single-admin-v1" }, "signature": "admin-signature"}Phase 1 may defer revocation UI, admin transfer, and epoch rotation UX to Phase 2, but it must not defer:
- signature verification on the membership document
- checking that the remote session signer is a live member
- rejecting unknown or removed device IDs
- persisting the highest accepted membership epoch
- refusing to serve heads, fetch, or membership responses that reveal vault state before membership proof succeeds
Session Authentication
Section titled “Session Authentication”After libp2p establishes a secure stream, the peers perform an application-level vault session handshake:
1. Initiator sends a signed AuthStart containing the epoch-blinded repo alias, epoch and membership-document hash hints, nonce, live libp2p Peer ID, and member device ID.2. Responder resolves the alias, verifies the AuthStart signature against a current member, and rejects unknown or removed devices without disclosing vault state. If the initiator is one epoch behind, it may instead return the signed membership transition and require a fresh AuthStart.3. Responder returns a signed AuthChallenge containing its nonce, selected wire protocol 11, current epoch and document hash, device ID, and live Peer ID.4. Initiator verifies the challenge against its current signed membership document, then signs the complete start/challenge transcript.5. Both sides derive a transcript-bound session ID and direction-separated confirmation, request, and response keys from the current `epoch_secret` with HKDF-SHA256.6. Initiator sends AuthProof containing its transcript signature, original nonce, device ID, and HMAC-SHA256 key-confirmation tag.7. Responder verifies the signature, membership epoch, and confirmation tag, then returns AuthAccepted with the session ID and a distinct responder confirmation tag. Only then may either side exchange vault data.This handshake prevents a transport-only identity from being mistaken for a vault member and binds the authenticated local and remote libp2p Peer IDs to the application device identities. A path change between relay and direct transport does not change either authenticated Peer ID, so the authorized session can continue; any identity, membership, MAC, sequence, or delivery failure still destroys it.
Both sides must complete verification before handling heads, fetch, or membership requests that disclose vault-specific state. If either peer learns about a higher signed membership epoch during the session, it must re-run authorization against that epoch before serving additional requests.
Post-handshake requests start at sequence 1 and permit one in-flight request per directional session. Each request MAC binds the protocol version, transcript hash, session ID, direction, exact sequence, and canonical payload. The response uses the same sequence and also binds the request tag. Authenticated failures are returned as MACed NACK payloads. A bad MAC, replay, sequence gap, counter exhaustion, membership change, timeout, or response-delivery failure destroys the affected session.
Responders retain at most 64 authorized sessions and expire an idle session after five minutes. Each transport Peer ID may hold only one responder session.
The epoch secret is shared by the epoch’s members. Compromise of that secret and a captured signed handshake transcript can reconstruct that epoch’s application session keys; the application key schedule does not provide retrospective forward secrecy. Noise over TCP or TLS 1.3 over QUIC remains the transport confidentiality layer.
Relay Behavior
Section titled “Relay Behavior”Circuit Relay v2 is fallback transport only.
The client fallback, reservation renewal, epoch-scoped rendezvous
advertisement, direct-first dialing, connection-targeted DCUtR upgrade, in-flight request
preservation, and relay-preserving failure path are implemented. The packaged
lattice infrastructure run process provides a stable-identity, bounded,
stateless Circuit Relay v2 and AutoNAT v2 service with TCP/QUIC listeners,
OpenMetrics, systemd system/user units, and strict typed configuration. Clients
can also use compatible external libp2p relays.
Relay requirements:
- bounded reservation TTL
- bounded active circuits per Peer ID
- bounded bytes per circuit
- bounded stream lifetime
- backpressure on buffers
- no durable state
- no vault-specific authorization database
The relay never sees plaintext vault data. It may still retain encrypted traffic, so application payloads that can be useful offline must remain protected independently of the relay.
Testing Boundary
Section titled “Testing Boundary”Application tests should not try to prove libp2p mDNS, DHT, DCUtR, or relay internals.
Cargo-testable behavior should focus on:
- application session handshake acceptance and rejection
- protocol version negotiation
- head exchange state machines
- request computation
- resource transfer requirements
- unauthorized fetch rejection
- rollback and conflict handling
- deterministic in-memory transport traits for protocol tests
Root/admin integration scripts may test network namespaces, multicast, or containerized relay behavior, but those are outside the normal cargo test contract.
MVP Defaults
Section titled “MVP Defaults”These are target production-MVP defaults, not a statement that every row is already implemented. Current implementation status is described above.
| Capability | Default |
|---|---|
| local mDNS | enabled |
| cached peer addresses | enabled |
| scoped rendezvous | enabled when configured |
| direct QUIC/TCP | enabled |
| AutoNAT | enabled |
| hole punching / DCUtR | enabled when supported |
| Circuit Relay v2 | enabled as fallback |
| public DHT/provider records | disabled |
| public gossipsub head announcements | disabled |
| mailbox polling | disabled |
| object storage polling | disabled |
Success Criteria
Section titled “Success Criteria”- Two online paired devices can synchronize across different networks when direct or relayed connectivity exists.
- Relays store no sync state.
- A transport-known but non-member peer cannot fetch vault data.
- Clients verify signed heads and membership before accepting fetched data.
- Protocol behavior can be tested with deterministic in-process transport traits.