Skip to content

[Tracking] Convergent peer reachability and recovery foundation #4554

Description

@M4n5ter
English

Thesis

Maka should have one peer reachability and connection-recovery foundation shared by Peer Mesh, direct Runtime Host profiles, and Session collaboration.

That foundation must converge automatically whenever enough reachability information still exists, and it must report an explicit repair state when automatic convergence is impossible. It must not hide a missing rendezvous path behind retries, long spinners, stale online indicators, or Session-specific workarounds.

This issue follows the completed Peer Mesh tracker #3842 and the WebRTC direct-path tracker #4382. Those projects supplied membership, signed Mesh routes, Circuit Relay v2 coordination, bounded member transit, QUIC/DCUtR, and WebRTC ICE. The remaining problem is that these capabilities do not yet form one durable recovery model.

The liveness boundary

A PeerId authenticates a peer; it does not locate that peer.

Two peers can rediscover each other after both have been offline and their addresses have changed only if at least one of the following remains true:

  1. one cached locator is still usable;
  2. a peer reacquires a coordination anchor whose address the other peer still knows; or
  3. a reachable third member or rendezvous service knows a current locator.

If all three are false, retries, a longer TTL, random DHT walks, DCUtR, WebRTC, and more UI waiting cannot create a first packet path. The correct state is needs_repair, followed by a fresh invitation or another explicit bootstrap path. Repair updates reachability for the existing identity and membership; it must not create a duplicate Mesh or silently replace authority.

This is the central invariant of the proposal. Any stronger availability claim requires an explicit rendezvous service and its accompanying privacy, abuse, operations, and SLA commitments.

Problems this model resolves

  • A Mesh invitation usually works because it contains fresh bootstrap routes, while a later Session invitation can fail after routes or relay reservations change.
  • Two members that go offline past route expiry can become a zombie Mesh if both addresses change and neither can reacquire a known relay anchor.
  • Route refresh currently sits in front of some connection attempts, so discovery latency delays useful dialing instead of contributing candidates concurrently.
  • Reconnect backoff is not always woken by a new route revision, network resume, or an already-restored peer path; switching Sessions can therefore recover faster than waiting.
  • Generic reachability facts currently share a record with Mesh presentation and transit fields. This couples unrelated lifecycles and prevents one resolver from serving Host profiles, Mesh control, and collaboration.
  • Collaboration credential finalization intentionally requires a second authenticated Runtime Host stream, but the UI presents it as a second full network preparation and can let the Owner appear finished before the Guest is ready.
  • Route expiry is sometimes perceived as membership expiry even though authorization and reachability are different facts.

Non-negotiable boundaries

  1. One peer endpoint and one PeerId per endpoint. native/runtime-host-peer remains the production connection authority. WebRTC does not introduce a JS sidecar, second Swarm, second reconnect loop, or second identity.
  2. Connectivity grants no Runtime Host authority. Mesh membership, Runtime Host credential/Root ID validation, and Session grants remain separate authorities.
  3. Membership is durable; reachability is leased. Losing every current route never removes membership or changes identity.
  4. One application protocol. QUIC, TCP, WebRTC, and approved member transit all terminate in the existing authenticated Runtime Host protocol.
  5. Public relays are coordination-only. Dynamically discovered or manually configured external Circuit Relay v2 nodes may carry Mesh control and direct-upgrade signaling, but never Runtime Host application traffic. Only an explicitly approved Mesh member may provide application transit.
  6. No silent mutation replay. Reconnection restores subscriptions and projections. It does not resend an operation whose outcome is unknown.
  7. A device is not an identity shortcut. Desktop Client and Runtime Host endpoints may live on the same computer but have different identities, lifecycles, and authority. UX may group them; the protocol must not merge them implicitly.

Target model

1. A reusable signed reachability lease

Introduce one peer-owned, self-signed PeerReachabilityLease independent of Mesh and Runtime Host application state:

PeerReachabilityLease
├── peerId
├── restart-safe monotonic revision
├── issuedAt / expiresAt
├── bounded direct routes
└── bounded accepted coordination-relay routes

The route classes are explicit. A coordination-relay route cannot accidentally become an application-capable route.

The lease contains no Mesh ID, display name, endpoint kind, transit policy, Runtime Host Root ID, credential, Session ID, or grant. A recipient verifies the signature, PeerId, revision, bounds, and expiry before treating it as current. Historical leases may be retained for a bounded recovery horizon as low-priority bootstrap hints, but they are never displayed as online or accepted as current truth.

This remains a Maka-typed signed record. A plain libp2p Signed Peer Record carries signed addresses, but does not express issuer expiry or the distinction between direct and coordination-only routes. We can adapt the Maka lease at a future rendezvous boundary without weakening the internal contract.

2. Mesh-specific member metadata stays Mesh-specific

The authority-signed Mesh roster remains the only membership authority. A separate peer-signed MeshMemberAdvertisement may carry the member alias, endpoint kind, and bounded capabilities for one Mesh.

Transit permission remains explicit Mesh policy. It must not be embedded in generic reachability or leak from one Mesh into another. A peer must be in the current roster before it can receive route or member-advertisement updates.

3. Persist coordination anchors, not reservations

For each persistent PeerId, remember a bounded set of previously successful relay addresses. On restart:

  1. apply manual relay configuration first;
  2. try to reacquire reservations on remembered successful anchors;
  3. use public IPFS Amino discovery only to replenish failed or missing slots;
  4. publish a relay route only after the main peer endpoint receives reservation acceptance.

The persisted fact is “this relay accepted us before,” not “the old reservation is still valid.” Reservation lifetime remains owned by the running endpoint.

Selection should be stable rather than RTT-churned: acquire a replacement before a planned release, apply bounded exponential backoff after refusal, and obey relay limits. The default active reservation count is a resource budget and redundancy policy, not a liveness proof. Increasing it may improve probability but also consumes third-party relay capacity; it should be changed from evidence, not intuition.

IPFS Amino remains an experimental, replaceable source of ordinary relay candidates. Maka does not publish peer presence to the public IPFS DHT and does not pretend that the DHT has a standard “find relays” or “find this Maka peer” query.

4. Symmetric, bounded anti-entropy

Mesh route exchange remains authenticated point-to-point anti-entropy, not Gossipsub:

  • every active member, including the authority, initiates reconciliation;
  • a replica prioritizes the authority and rotates through other current members;
  • the authority rotates through current members instead of waiting passively;
  • a material reachability revision, accepted/lost relay reservation, network resume, or successful repair triggers immediate bounded sync;
  • a low-frequency periodic pass handles missed events;
  • only current-roster peers exchange fresh state;
  • duplicate or older revisions are ignored;
  • expired signed routes may be tried only as rate-limited identity-verified bootstrap hints within the recovery horizon.

This is sufficient for small private Meshes and keeps message volume bounded. Gossipsub would still need discovery and would add overlay maintenance, scoring, and duplicate state without solving the zero-locator case.

5. One event-fed connection attempt authority

Host profiles, Mesh control, and Session invitations all use the same resolver and attempt lifecycle:

cached / invitation / historical hints ──┐
fresh Mesh anti-entropy updates ─────────┤
reacquired relay anchors ────────────────┼──> one fenced attempt
direct observation and upgrades ─────────┘        │
                                                  ├── QUIC/TCP direct
                                                  ├── QUIC + DCUtR
                                                  ├── WebRTC + ICE
                                                  └── approved member transit

Known routes are dialed immediately. Discovery and anti-entropy run concurrently and feed newly verified candidates into the same target-bound attempt. There is no serial “discover for N seconds, then start connecting” gate.

The attempt has one immutable expected PeerId, one deadline, one cancellation source, one winner, and stale-result fencing. A route revision, network resume, explicit retry, or restored underlying peer connection wakes recovery immediately instead of waiting for an old backoff timer. Backoff still protects unavailable peers and resources when no new evidence exists.

The winner only provides transport. The existing Runtime Host handshake must still validate the expected PeerId, Root ID, compatibility, and credential before application use.

6. Session collaboration uses current reachability, not a frozen invitation

A Session invitation remains an application-authority payload plus bounded bootstrap hints. It identifies the target Runtime Host PeerId/Root ID and Guest credential flow, but it is not a permanent route snapshot.

On initial join and every later reconnect, the Guest asks the shared resolver for the target peer's current verified routes from direct profile state and any joined Mesh. Route changes do not require resharing the Session. Mesh removal does not revoke Session authority, and Session revocation does not remove Mesh membership.

The collaboration security flow remains two application-authentication phases:

  1. a narrowly scoped pending credential claims and binds the invitation;
  2. a fresh Runtime Host stream authenticates the active, client-bound Guest credential and catches up the shared projection.

This must not become two network acquisitions. The underlying authenticated peer connection is retained when healthy; only the Runtime Host stream is reopened. UX presents one continuous join with honest sub-states such as “Securing access to this device” and “Loading shared task.” The Owner may show “Guest admitted” after finalization, but “Guest ready” is not true until the second stream is authenticated and transcript catch-up reaches its watermark.

7. Explicit recovery state

Expose a small product state machine rather than protocol trivia:

connecting → reachable
     │           │
     ├── new evidence wakes attempt
     ├── transient loss → reconnecting
     └── no locator after bounded recovery → needs_repair

“Direct,” “WebRTC,” relay reservation counts, STUN, and failure reason codes belong in progressive diagnostics. Normal UX answers: is the peer available, is Maka trying, or does the user need a new invitation/bootstrap action?

Closing, leaving, cancelling, and discarding pairing must remain locally responsive. A remote acknowledgement may complete later, but a missing remote path must not trap local state behind an indefinite modal or lock.

Why not the tempting alternatives?

  • Deterministic PeerId → DHT region → common relay: DHT topology, relay admission, and time windows do not guarantee that two peers select the same usable relay. It is a heuristic, not a liveness foundation.
  • Publish Maka presence in the public IPFS DHT: this expands metadata leakage and abuse surface while still providing no project-controlled availability guarantee.
  • Gossipsub: it distributes data after peers meet; it does not provide the missing first locator.
  • Treat expired routes as dead membership: this destroys durable authority because of a temporary network fact.
  • Persist reservation state: a reservation belongs to a live connection and can be refused after restart. Only the anchor address and success history are reusable.
  • Wait for discovery before dialing: it increases latency and makes slow discovery indistinguishable from a hung connection.
  • Merge Desktop and Runtime Host identities: it couples different authority and lifecycle domains and makes remote Host ownership ambiguous.
  • A production js-libp2p sidecar: it duplicates identity, Swarm, lifecycle, resource limits, packaging, and recovery ownership. JavaScript remains an interoperability oracle only.
  • A mandatory central service now: it would offer stronger rendezvous guarantees, but it is a separate product and operations commitment. We should add it only when we choose that SLA explicitly.

Availability claim

This design guarantees bounded behavior and convergence under stated preconditions, not universal connectivity:

  • With a usable cached route, a reacquired remembered anchor, or a reachable current member, peers converge without a new invitation.
  • If direct punching fails, an explicitly approved member may carry application traffic.
  • If every shared locator is gone, Maka reaches needs_repair promptly and preserves identity, membership, and grants for repair.
  • Guaranteed recovery after arbitrary simultaneous downtime and total address churn requires a stable rendezvous service. Standard libp2p Rendezvous is currently a Working Draft and leaves deployment/abuse policy to operators, so adopting it is a later explicit decision rather than a hidden dependency.

Security and privacy properties

  • Every reachability lease is self-signed and target PeerId authentication still occurs on connection.
  • Mesh roster validation precedes state exchange; removed peers receive no new routes.
  • Historical hints are bounded, rate-limited, and cannot establish identity by themselves.
  • Routes are network metadata and are shared only through explicit invitations or authenticated Mesh control; diagnostics redact them by default.
  • Public relay discovery sees the discovery Swarm's temporary identity, while reservation and publication remain owned by the real peer endpoint.
  • External Circuit Relay v2 nodes never become application authorities and never receive Runtime Host plaintext/application streams.
  • Reconnect and path changes never weaken credential, Root ID, grant, or plaintext policy.

Delivery plan

This feature has not shipped, so prefer a clean internal model over dual-read/dual-write compatibility paths.

  1. Reachability domain split — introduce the reusable lease, move Mesh presentation/transit fields to Mesh-specific state, and make every consumer use typed direct vs coordination routes.
  2. Stable anchor recovery — persist bounded successful relay anchors per PeerId, reacquire them before public discovery, and publish native reachability revisions.
  3. Convergent Mesh control — make anti-entropy symmetric and event-driven, add bounded historical bootstrap hints, and define needs_repair plus invitation-based repair of existing membership.
  4. Unified recovery and collaboration — feed route revisions into one connection attempt/reconnect lifecycle, reuse the underlying peer connection across Guest finalization, and project authoritative readiness states to both Owner and Guest.

Each layer should be independently reviewable, but the acceptance matrix below is the release gate for the whole model.

High-value acceptance matrix

Keep tests small and decision-relevant:

  1. Remembered-anchor convergence: two peers stop, direct addresses change, both restart, and at least one advertised relay anchor can be reacquired. They rediscover each other and refresh signed leases without a new invitation.
  2. Third-member convergence: the authority has no usable route to a replica, but another current member has a fresher signed lease. Bounded anti-entropy restores contact without granting that member authority.
  3. Honest zero-locator failure and repair: all cached routes and anchors are unusable and no third member exists. Recovery reaches needs_repair within a bound; a fresh invitation repairs the existing membership/identity rather than creating a duplicate.
  4. Event-driven Session recovery: a Guest loses its path, a new signed route arrives, and recovery wakes immediately. Subscription catch-up reaches one canonical watermark, no accepted mutation is replayed, and Owner/Guest agree on admitted/ready/terminal state.
  5. Single visible join: collaboration credential finalization opens a second Runtime Host stream over the existing peer connection. The UI does not repeat network preparation, and “ready” is emitted only after authenticated catch-up.
  6. Resource and authority bounds: candidate, route, historical-hint, connection-attempt, and backoff limits hold under churn; removed members cannot pull routes; public relays carry no Runtime Host application bytes.

The controlled NAT harness from #4382 should exercise topology-dependent behavior outside ordinary CI. Deterministic unit/integration tests cover state, fencing, recovery wakeups, and authorization. Public-network success rates must not become flaky merge gates.

Related evidence and specifications

Definition of done

The issue is complete when Peer Mesh, direct Host profiles, and Session collaboration consume one reachability/recovery model; automatic reconnection converges whenever the liveness precondition holds; impossible recovery becomes a prompt, bounded needs_repair state; and the security, resource, no-replay, and cross-platform acceptance matrix passes without introducing a second network authority.

简体中文

核心结论

Maka 需要一套统一的 peer 可达性与连接恢复基座,让 Peer Mesh、Runtime Host 直连配置和 Session 协作都走同一套模型。

只要网络里还留着足够的信息,系统就应该自己重新收敛;如果客观上已经没有任何见面的路径,就要尽快告诉用户“需要修复”,而不是一直重试、转圈、显示过期的在线状态,或者在 Session 功能里再补一套临时逻辑。

这个 issue 承接已经完成的 Peer Mesh #3842 和 WebRTC 直连 #4382。前面的工作已经有了成员关系、签名路由、Circuit Relay v2 协调、受控的成员转发、QUIC/DCUtR 和 WebRTC ICE。现在缺的不是再加一种传输协议,而是把这些能力收进一套可靠的恢复模型。

先把能力边界说清楚

PeerId 能证明“对面是谁”,但不能告诉我们“对面在哪儿”。

两个节点都离线过、地址也变了以后,想自动重新找到彼此,至少要满足下面一条:

  1. 手里还有一条能用的旧地址;
  2. 某一方重新拿到了一个对方认识的协调节点 reservation;
  3. 还有一个能联系到的 Mesh 成员或 rendezvous 服务,知道当前地址。

如果三条都不成立,那么加大重试次数、延长 TTL、随机扫 DHT、跑 DCUtR 或 WebRTC 都不会凭空变出第一条链路。此时正确行为是进入 needs_repair,让用户用新邀请或其他明确入口补回 bootstrap 信息。修复的是原来的身份和成员关系,不能偷偷新建一个重复 Mesh,也不能换掉原来的 authority。

这是整套设计最重要的不变量。如果我们以后承诺“无论双方离线多久、地址怎么变都能自动回来”,那就必须明确引入一个稳定 rendezvous 服务,并正面承担隐私、滥用防护、运维和 SLA,而不能把这份承诺藏在客户端算法里。

这套模型要解决什么

  • 加入 Mesh 往往很顺,因为邀请里带的是新鲜地址;过一阵再加入共享 Session,却可能拿着已经失效的地址反复失败。
  • 两个成员都离线到路由过期,回来后地址又变了,如果谁也拿不回对方认识的 relay,就会形成“成员还在、永远见不到”的僵尸 Mesh。
  • 有些连接会先等路由刷新,再真正拨号。于是明明手里有候选地址,也要陪 discovery 一起等。
  • 新路由、系统唤醒或底层连接已经恢复时,reconnect 不一定马上被叫醒;切走 Session 再切回来反而更快。
  • 通用可达信息和 Mesh 别名、节点类型、transit 字段挤在同一份 record 里,生命周期彼此牵连,也没法让 Host profile、Mesh control 和协作共用一个 resolver。
  • Guest 凭据 finalization 出于安全原因确实要再开一条 Runtime Host stream,但现在看起来像网络连接完整跑了两遍;Owner 甚至可能先看到“完成”,Guest 还在第二轮加载。
  • 路由过期有时被表现得像成员过期,但网络状态不应该改写成员 authority。

不能破坏的边界

  1. 一个 endpoint 只有一个 PeerId,也只有一个连接 authority。 native/runtime-host-peer 继续拥有生产连接。WebRTC 不能带来 JS sidecar、第二个 Swarm、第二套重连循环或第二个身份。
  2. 连得上不等于有权限。 Mesh membership、Runtime Host credential/Root ID、Session grant 继续各管各的。
  3. 成员关系是长期事实,可达性是短租约。 当前没有路由,只代表暂时找不到,不能删成员、换身份。
  4. 上层只有一套应用协议。 QUIC、TCP、WebRTC 和获批的成员转发,最后都进入同一个经过认证的 Runtime Host protocol。
  5. 公共 relay 只负责协调。 动态找到或手动填写的外部 Circuit Relay v2 可以承载 Mesh control 和打洞 signaling,但不能转发 Runtime Host 应用流量。只有用户明确开启的 Mesh member transit 才能做应用 fallback。
  6. 断线不能偷偷重放操作。 恢复连接可以重建订阅和 projection,但结果未知的 mutation 不能自动再发一次。
  7. 一台电脑不等于一个协议身份。 Desktop Client 和 Runtime Host 即使在同一台机器上,也有不同生命周期和权限。UI 可以把它们归在一起展示,底层不能暗中合并身份。

目标模型

1. 一份真正通用的签名可达租约

增加 peer 自己签名的 PeerReachabilityLease,只描述“如何找到这个 PeerId”,不掺 Mesh 和 Runtime Host 业务状态:

PeerReachabilityLease
├── peerId
├── 跨重启仍单调递增的 revision
├── issuedAt / expiresAt
├── 有界的 direct routes
└── 有界的、已经接受 reservation 的 coordination-relay routes

两类 route 必须在类型上分开,不能让协调 relay 地址误入应用流量路径。

这份租约不带 Mesh ID、别名、节点类型、transit 策略、Runtime Host Root ID、credential、Session ID 或 grant。接收方验证签名、PeerId、revision、数量边界和有效期以后,才能把它当成当前事实。过期租约可以在一个有限恢复窗口内作为低优先级 bootstrap 线索,但绝不能因此显示“在线”,也不能把旧信息冒充新状态。

内部继续使用 Maka 自己定义的签名 record。libp2p 的 Signed Peer Record 能签地址,但没有 issuer expiry,也表达不了“直连地址”和“仅协调地址”的区别。以后接入 rendezvous 时可以做适配,没有必要为了表面标准化削弱内部契约。

2. Mesh 的信息留在 Mesh 里

authority 签名的 roster 继续是唯一成员 authority。每个 peer 可以另外签一份 MeshMemberAdvertisement,只放这个 Mesh 里的别名、endpoint 类型和少量能力信息。

transit 是否允许属于 Mesh policy,不能塞进通用 reachability,更不能从一个 Mesh 泄漏到另一个 Mesh。只有当前 roster 里的成员,才有资格收发新的路由和成员信息。

3. 记住协调锚点,不假装 reservation 永久有效

每个持久 PeerId 记住少量曾经成功过的 relay 地址。进程重启后按下面顺序恢复:

  1. 先应用用户手动配置的 relay;
  2. 尝试在以前成功过的 relay 上重新申请 reservation;
  3. 缺口再交给 IPFS Amino 公网发现补充;
  4. 只有主 peer endpoint 真正收到 reservation accepted,才把地址发布出去。

持久化的是“这台 relay 以前接受过我”,不是“上次 reservation 现在还活着”。reservation 始终属于当前运行中的 endpoint。

选 relay 应该以稳定为主,不要为了几十毫秒 RTT 反复换节点。计划替换时先拿到新的再放旧的;遭到拒绝后做有界指数退避;尊重对方资源限制。默认保留几条 active reservation 是资源预算和冗余策略,不是可用性证明。数量更多可能提高概率,也会消耗第三方容量,应该拿数据决定,而不是凭感觉堆数量。

IPFS Amino 只是一种可替换的实验性 relay 候选来源。Maka 不把自己的在线位置发布到公共 IPFS DHT,也不虚构 DHT 存在“查找所有 relay”或“查找某个 Maka peer”的标准接口。

4. 双向、有界的 anti-entropy

Mesh 继续使用认证后的点对点同步,不上 Gossipsub:

  • 所有 active member 都主动同步,authority 也不能只等别人找上门;
  • replica 优先找 authority,同时轮询其他当前成员;
  • authority 也轮询当前成员;
  • route revision 变化、relay reservation 增减、网络恢复或人工 repair 成功时,马上触发一次有界同步;
  • 再保留一个低频周期任务,兜底漏掉的事件;
  • 只有 current roster 成员能交换 fresh state;
  • 重复或更旧 revision 直接忽略;
  • 过期签名路由只能在恢复窗口内做限速 bootstrap,连接仍必须重新验证 PeerId。

小型私有 Mesh 用这套办法已经够了,而且消息量有明确上界。Gossipsub 仍然要先解决 peer discovery,还会带来 overlay、scoring 和消息去重状态,并不能解决“双方一个地址都没有”的根问题。

5. 一个可以持续吃进新证据的 connection attempt

Host profile、Mesh control 和 Session invitation 全部使用同一个 resolver 和 attempt lifecycle:

缓存 / 邀请 / 历史 hints ───────────┐
Mesh anti-entropy 新路由 ───────────┤
重新取得的 relay anchor ────────────┼──> 一个带 fence 的 attempt
direct observation / upgrade ──────┘          │
                                               ├── QUIC/TCP direct
                                               ├── QUIC + DCUtR
                                               ├── WebRTC + ICE
                                               └── 获批 member transit

手里已有的地址立即拨号,discovery 和 anti-entropy 同时跑,后续拿到的新候选直接喂给同一个 target-bound attempt。不能再有“先 discovery 十秒,再开始连接”的串行门。

一次 attempt 只有一个 immutable expected PeerId、一个 deadline、一个取消源、一个 winner,并且拒绝旧 attempt 的迟到结果。route revision、网络唤醒、用户重试或底层 peer connection 恢复时,recovery 要立即醒来,不能继续睡在旧 backoff 里。没有任何新证据时,backoff 仍负责保护不可达 peer 和本机资源。

winning path 只解决 transport。真正交给应用前,现有 Runtime Host handshake 仍要验证 expected PeerId、Root ID、compatibility 和 credential。

6. Session 协作永远解析当前路由

Session invitation 仍然包含应用 authority payload 和少量 bootstrap hints,但不能成为永久路由快照。它标出目标 Runtime Host 的 PeerId/Root ID,并携带 Guest credential 流程所需信息;以后每次重连都要按目标 PeerId 向统一 resolver 查询当前可用路由,包括 direct profile 和已经加入的 Mesh。

这样,路由变了不需要重新分享 Session。退出 Mesh 不会自动撤销 Session 权限,撤销 Session 也不会把节点踢出 Mesh。

协作认证保留两段式:

  1. 权限极小的 pending credential 领取邀请,并绑定当前 Client;
  2. 新开一条 Runtime Host stream,用已经绑定的正式 Guest credential 认证,再追上共享 transcript。

这是两次应用认证,不应该变成两次找网络。底层 peer connection 健康时要直接复用,只重开 Runtime Host stream。UI 把它表现成一个连续过程,例如“正在保护此设备的访问权限”然后“正在载入共享任务”,不能再完整跑两遍“准备可用路径”。Owner 在 finalization 后可以看到“Guest 已准入”,但第二条 stream 完成认证、transcript 追到 watermark 以前,不能显示“Guest 已就绪”。

7. 明确的恢复状态

普通用户只需要理解一个小状态机:

连接中 → 可用
  │        │
  ├── 新证据会立即唤醒连接
  ├── 短暂掉线 → 正在重连
  └── 有界恢复后仍无地址 → 需要修复

Direct、WebRTC、reservation 数量、STUN 和具体错误码放进渐进式诊断。主界面只回答三个问题:现在能不能用、Maka 是否正在恢复、是否需要用户提供一份新邀请或 bootstrap。

关闭、退出、取消和放弃配对必须优先完成本地动作。远端确认可以稍后补齐,不能因为对面联系不上,就把用户困在无法关闭的弹窗或本地锁里。

为什么不选那些看起来更省事的方案

  • PeerId 映射到 DHT 区域,再让双方碰巧挑到同一台 relay: DHT 拓扑、relay 是否接受 reservation、双方上线时间都无法保证收敛。这可以做 heuristic,不能做基座。
  • 把 Maka 在线信息发布到公共 IPFS DHT: 暴露更多网络元数据和滥用面,仍然得不到 Maka 自己能承诺的可用性。
  • Gossipsub: 它解决的是见面以后怎么散播消息,不会提供第一条 locator。
  • 路由过期就当成员失效: 用短暂网络事实破坏长期 authority,概念上就是错的。
  • 把 reservation 本身持久化: reservation 属于 live connection,重启后对方完全可以拒绝。能复用的只有 anchor 地址和成功历史。
  • 先等 discovery,再拨号: 白白增加延迟,也让慢 discovery 看起来像程序卡死。
  • 把 Desktop 和 Runtime Host 身份合并: 两者权限和生命周期不同,强行合并只会让远程 Host 的归属更模糊。
  • 生产环境塞一个 js-libp2p sidecar: 会重复 PeerId、Swarm、生命周期、资源限制、打包和恢复 authority。JavaScript 只保留为互操作测试对照。
  • 现在就强制依赖中心服务: 中心 rendezvous 的确能给更强保证,但这是另一项产品和运维承诺。只有当我们明确选择这份 SLA 时才引入。

我们实际承诺什么

这套设计承诺的是“在条件成立时自动收敛、条件不成立时有界失败”,不是万能网络:

  • 只要还有可用缓存地址、能重新拿回的历史 anchor,或者能联系到的当前成员,就不需要新邀请,系统会自己回来。
  • 直连打不通时,可以使用用户明确批准的 member transit。
  • 所有共同 locator 都没了,就及时进入 needs_repair,同时保留身份、membership 和 grant,等待修复。
  • 如果未来要保证任意长时间的双端离线和彻底地址变化后仍能自动恢复,就必须引入稳定 rendezvous。libp2p Rendezvous 目前仍是 Working Draft,部署和滥用策略也由运营方负责,因此它只能是以后明确批准的能力,不能暗中成为今天的依赖。

安全与隐私

  • reachability lease 必须由 PeerId 自签,真正连接时还要再次认证目标 PeerId。
  • 先检查 Mesh roster,再交换状态;已经移除的成员拿不到新路由。
  • 历史 hint 有时间、数量和频率上限,自己不能证明身份。
  • 路由属于敏感网络元数据,只能通过明确邀请或认证后的 Mesh control 分享,普通诊断默认脱敏。
  • 公网 relay discovery 使用临时 discovery identity;真正 reservation 和对外发布仍由主 endpoint 独占。
  • 外部 Circuit Relay v2 永远不是应用 authority,也不能承载 Runtime Host 明文或应用 stream。
  • 换路径和重连只能改变“怎么到达”,不能降低 credential、Root ID、grant 或 plaintext policy。

落地顺序

这组功能尚未正式发布,因此优先得到干净模型,不为内部半成品增加双读、双写和长期兼容层。

  1. 拆出 reachability domain:增加通用 lease,把 Mesh 展示/transit 字段迁回 Mesh 状态,所有消费者使用类型化的 direct/coordination route。
  2. 稳定 anchor 恢复:按 PeerId 持久保存少量成功 relay,启动时优先重新申请,并由 native endpoint 发出 reachability revision 事件。
  3. 让 Mesh 真正收敛:anti-entropy 改成双向、事件驱动;增加有界历史 bootstrap;定义 needs_repair,支持新邀请修复原 membership。
  4. 统一恢复与协作:新 route 直接进入同一个 connection/reconnect lifecycle;Guest finalization 复用底层 peer connection;Owner/Guest 都使用同一份权威 ready 状态。

每层都可以单独 review,但下面的验收矩阵必须整体通过,才能认为模型完成。

少量、高价值验收

  1. 历史 anchor 自动收敛: 两端停机、直连地址都变;重启后至少一条曾发布的 relay anchor 能重新申请成功。双方无需新邀请即可恢复联系,并交换新签名租约。
  2. 第三成员帮助收敛: authority 没有 replica 的可用地址,但另一个 current member 持有更新租约。anti-entropy 找回地址,同时这个成员不会因此获得 authority。
  3. 零 locator 时诚实失败并可修复: 缓存地址、历史 anchor 全部不可用,也没有第三成员。系统在时限内进入 needs_repair;粘贴新邀请后修复原 membership/identity,不产生重复项。
  4. Session 事件驱动恢复: Guest 断路后收到新签名 route,recovery 立即醒来;subscription 只追到一个 canonical watermark,不重放 mutation,Owner/Guest 对 admitted、ready、terminal 状态一致。
  5. 加入过程只跑一次网络准备: credential finalization 在原 peer connection 上重开 Runtime Host stream。UI 不重复网络准备,只有认证并 catch-up 完成才发出 ready。
  6. 资源和 authority 有界: candidate、route、历史 hint、attempt 和 backoff 在 churn 下都不失控;被移除成员无法拉取路由;公共 relay 上没有 Runtime Host 应用字节。

#4382 的受控 NAT harness 用于验证依赖 topology 的行为,不塞进普通 CI。普通单元/集成测试只锁定状态机、fence、recovery wakeup 和授权。公网成功率不能变成不稳定的合并门槛。

相关证据与规范

完成标准

当 Peer Mesh、Host 直连配置和 Session 协作都消费同一套 reachability/recovery 模型;满足可达前提时能自动收敛;客观无法恢复时能快速、准确地进入可修复状态;并且安全、资源上界、禁止重放和跨平台验收全部通过,同时没有引入第二个网络 authority,这个 issue 才算完成。

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions