Skip to content

feat(runtime-host): discover peer mesh routes - #4055

Merged
M4n5ter merged 8 commits into
mainfrom
feat/runtime-host-peer-mesh-discovery
Aug 28, 2026
Merged

feat(runtime-host): discover peer mesh routes#4055
M4n5ter merged 8 commits into
mainfrom
feat/runtime-host-peer-mesh-discovery

Conversation

@M4n5ter

@M4n5ter M4n5ter commented Aug 28, 2026

Copy link
Copy Markdown
Member

Summary

English

Peer Mesh members now publish short-lived route records signed by their libp2p identities and reconcile roster and route changes directly with one another. The shared peer endpoint can use the verified cache without changing the requested Peer ID; Runtime Host access still requires the existing credential.

This is the discovery and recovery layer of #3842 Milestone 2. Desktop/CLI management remains outside this stacked PR.

简体中文

Peer Mesh 成员现在会发布由自身 libp2p identity 签名的短期 route record,并通过点对点同步收敛 roster 和 route 变化。共享 peer endpoint 可以使用已验证的 route cache,但不会改变请求的 Peer ID;访问 Runtime Host 仍然必须持有现有 credential。

这是 #3842 Milestone 2 的发现与恢复层。Desktop/CLI 管理不在这个 stacked PR 的范围内。

Refs #3842

Verification

English
  • npm run typecheck
  • npm test --workspace @maka/runtime-host
  • npm run lint:runtime-host-peer
  • cargo test --manifest-path native/runtime-host-peer/Cargo.toml --all-features
  • npm run check:asf-headers
  • Targeted Biome checks
  • Built the release native addon, packed a development CLI tarball, and passed the offline installed-product smoke
简体中文
  • npm run typecheck
  • npm test --workspace @maka/runtime-host
  • npm run lint:runtime-host-peer
  • cargo test --manifest-path native/runtime-host-peer/Cargo.toml --all-features
  • npm run check:asf-headers
  • 针对变更文件的 Biome 检查
  • 构建 release 原生 addon、打包开发版 CLI tarball,并通过离线安装后的产品 smoke

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Codex assisted with implementation and verification under maintainer direction.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

@github-actions github-actions Bot added the effort/XL Over 1000 readable lines label Aug 28, 2026
@M4n5ter
M4n5ter force-pushed the feat/runtime-host-peer-mesh-discovery branch from f7405cc to 261da12 Compare August 28, 2026 04:07
@M4n5ter
M4n5ter force-pushed the feat/runtime-host-peer-mesh-discovery branch from 261da12 to 5f05f9a Compare August 28, 2026 04:15
Base automatically changed from feat/runtime-host-peer-mesh-membership to main August 28, 2026 04:40
@M4n5ter
M4n5ter force-pushed the feat/runtime-host-peer-mesh-discovery branch 2 times, most recently from 4cc2264 to 5672f50 Compare August 28, 2026 04:41
Sign short-lived route records with each libp2p identity and reconcile them between admitted peers. Feed only verified routes into the shared endpoint while preserving the target PeerId and Runtime Host credential authority.

Cover route changes, removal propagation, restart recovery, native identity binding, and the installed CLI composition.

Generated-by: Codex
Serve application and Mesh control traffic from one peer identity, preserve verified bootstrap routes, and keep removal convergence scoped to the removed member.

Generated-by: OpenAI Codex
Store each signed peer route once per node, filter it through active Mesh rosters, and allow expired route sequences to restart without weakening fresh-route rollback protection.

Generated-by: OpenAI Codex
Use the route resolver only for application connections so Mesh control has one target-composition authority.

Generated-by: OpenAI Codex
Reuse the node's local route refresh while joining so background reconciliation cannot publish a different fact at the same sequence.

Generated-by: OpenAI Codex
Allocate every local route sequence from the node-level cache instead of accepting caller-supplied values.

Generated-by: OpenAI Codex
@M4n5ter
M4n5ter force-pushed the feat/runtime-host-peer-mesh-discovery branch from 5672f50 to 16dd68d Compare August 28, 2026 05:37
Queue application and Mesh-control dials targeting the same PeerId so the shared native endpoint never rejects a valid concurrent caller as already in progress.

Generated-by: OpenAI Codex
@M4n5ter
M4n5ter marked this pull request as ready for review August 28, 2026 06:56

@jackwener jackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this feature is ready to merge at 1ee1f08594a85d92b7daea5d51ccb385c1eeac4d. The identity and credential boundaries look sound, but the claimed behavior is not connected to the product, and the reconciliation loop has a repeatable partition failure.

[P1] Start the Mesh owner on the supported product path

openRuntimeHostPeerMeshOwner() is currently only defined/exported and called by tests and the installed-package smoke. Desktop still calls createRuntimeHostPeerClientFromEnvironment() without a routeResolver (apps/desktop/src/main/runtime-host-boot.ts:224-226), CLI does the same through its default dependency (packages/cli/src/runtime-host-cli-context.ts:138-141), and the Host listener creates a bare client unless a caller explicitly supplies a shared one (packages/runtime-host/src/server/peer-listener.ts:56-68).

As a result, normal Desktop/CLI startup neither serves Mesh control nor publishes, persists, or consumes these signed route records. Connections continue to use profile route hints and coordination relays, while the new durable route cache is another address authority that the product does not operate. The smoke proves the new library composition by manually opening two owners; it does not exercise a supported application startup.

Please make one real product owner create the shared endpoint, pass its client to the Host listener and application connector, and cover restart/recovery through that startup path. If this is intentionally only an unshipped library layer, the PR should say that clearly instead of claiming that Peer Mesh members now publish and reconcile routes.

[P2] Refresh the local signed route during long reconciliation rounds

reconcile() signs one five-minute local route before iterating over sorted targets serially. Each unreachable target may consume the 30-second dial deadline, and every later sync reuses that original proof. With eleven earlier offline members, a healthy later member receives the proof after it has expired, rejects the sync, and stays on the old roster. The next round signs once and follows the same order, so this can repeat indefinitely.

A 14-node fault-injection probe reproduced this for two consecutive rounds: the authority reached roster revision 15 while the healthy member remained at 14 after 22 * 31s of simulated dial time. Refreshing the local route inside the target loop made the same probe converge to revision 15. Please ensure the proof is still within its refresh window before each target/page; bounded concurrency can address the total reconciliation budget separately.

[P2] Establish that a second signed address protocol is necessary

The current direct-peer path already persists operator-supplied route hints and coordination relays, with Identify/DCUtR available after connection. This PR adds a second short-lived signed document, durable route store, paging, gossip, and reconciliation state. The three-peer tests use an in-memory transport, while the native smoke uses loopback and deliberately supplies a wrong hint so the new resolver wins. Neither can show that relay-only recovery or updating the existing profile authority is insufficient after a real address/NAT change.

Please add a real endpoint comparison across an address change: peer ID only, stale route hint, coordination relay only, and signed-cache recovery. If the relay-only case succeeds, the smaller next step may be improving the existing profile/hint recovery path rather than introducing another route authority.

Lower-priority consistency issues

  • mergeAuthenticatedRoute() removes the existing same-peer record before calling mergeRoutes(), so the shared equal-sequence/different-fact check cannot run on that authenticated path (packages/runtime-host/src/peer-mesh/node.ts:1140-1157). Sync/gossip detects the same conflict. Passing the unfiltered set into the shared merge logic would keep both paths consistent.
  • Same-peer connection requests are queued in TypeScript, but Rust returns peer_connect_in_progress when the prior application stream is already active (native/runtime-host-peer/src/engine.rs:447-455). If one active application stream per peer is intentional, use an already-connected result/message so callers are not told that retrying an in-progress operation may succeed.

I also verified important positive controls: a real Rust probe bound the claimed Peer ID to the supplied public key and signature and rejected both foreign identities and tampered payloads; a second native probe showed that route hints cannot change the caller's requested Peer ID; cancellation and owner-close cleanup are scoped to the request/connection they own; and an end-to-end native probe kept Runtime Host credential rejection independent from route discovery. The full Runtime Host suite passed 1,314 tests with nine environment skips, and all applicable exact-head hosted checks are green. These controls do not close the product-wiring and convergence findings above.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

Refresh the local signed route before each synchronization page so earlier unreachable members cannot starve later healthy peers with an expired proof. Add a deterministic long-round regression covering the real per-peer dial budget.

Generated-by: OpenAI Codex
@M4n5ter

M4n5ter commented Aug 28, 2026

Copy link
Copy Markdown
Member Author
English

Thanks for the detailed review. I adjudicated each point against the staged M2 boundary and the current implementation.

Confirmed and fixed

The long-round route-expiry finding is correct. Reconciliation refreshed the local route only once before a serial target walk, so enough earlier 30-second dial failures could make the proof expire before a later healthy member received it. Commit 8bc4f7bb3 now refreshes and re-reads the local route before every synchronization page.

The regression uses the real bound rather than an arbitrary clock jump: eleven earlier unreachable peers each consume 31 seconds, after which a later healthy peer must still advance from roster revision 13 to 14. It fails without the fix and passes with it. The complete Runtime Host suite passes 1,315 tests with nine environment skips.

Findings not accepted

  • Desktop/CLI startup wiring: feat(runtime-host): discover peer mesh routes #4055 is deliberately the discovery/recovery layer in the staged M2 plan, and the PR body already states that Desktop/CLI management is outside this stacked PR. openRuntimeHostPeerMeshOwner() is the single composition seam, while the installed-package smoke proves that one shared endpoint serves Mesh control, the Host listener, and the application connector. Product startup and management wiring belong to the next Desktop/CLI slice; pulling them into this PR would collapse the review boundary rather than fix a defect.

  • Necessity of signed route records: existing profile hints are operator-supplied mutable hints; Identify/DCUtR cannot bootstrap a connection after every known address becomes stale. A coordination Relay can provide bootstrap only when one is configured and available, while the approved contract also supports Relay-disabled operation. The signed cache is not another target or identity authority: it only contributes mutable routes, and the connector still pins the requested PeerId. The deterministic three-peer propagation test plus the installed native wrong-hint connection prove this layer's invariant. The full Desktop/Linux address-change journey remains part of the next product slice.

  • Authenticated equal-sequence merge: I tested the suggested unification with the generic merge path, and it breaks an existing high-value recovery case. If invitation redemption commits but its response is lost, the not-yet-joined peer has no durable membership route and must retry by signing a fresh short-lived route with the same initial sequence. The authority has authenticated the same PeerId and verified that signature, so this narrow redemption path may replace that fact; it still rejects a lower sequence while the existing route is live. Sync/gossip remains strict. Keeping these paths distinct is required for committed-redemption idempotency.

  • Active application stream error: the one-active-application-stream-per-PeerId constraint predates this PR. The TypeScript queue added here serializes concurrent dial attempts across application and Mesh control; it cannot hand an already-owned framed application stream to another caller. If product evidence later calls for clearer duplicate-connection diagnostics, that should be handled by connection-owner deduplication or a distinct error code, not by changing this Mesh discovery slice.

简体中文

感谢详细审查。我根据 M2 的分阶段边界和当前实现逐项进行了裁决。

已确认并修复

长轮次导致 route proof 过期的问题成立。此前 reconciliation 只在串行遍历 targets 前刷新一次本地 route;足够多的前序 30 秒连接失败会使 proof 在到达后序健康成员前过期。提交 8bc4f7bb3 现在会在每个同步页面开始前刷新并重新读取本地 route。

回归测试使用真实预算,而不是任意跳时:11 个前序不可达 peer 各消耗 31 秒,随后健康 peer 仍必须从 roster revision 13 收敛到 14。该测试在没有修复时失败,修复后通过。完整 Runtime Host suite 为 1,315 项通过、9 项环境跳过。

未采纳的 findings

  • Desktop/CLI 启动接线: feat(runtime-host): discover peer mesh routes #4055 按 M2 分阶段计划只交付 discovery/recovery layer,PR body 也已明确 Desktop/CLI management 不在这个 stacked PR 内。openRuntimeHostPeerMeshOwner() 是唯一 composition seam;installed-package smoke 已证明同一个 endpoint 同时承载 Mesh control、Host listener 和 application connector。产品启动与管理接线属于下一条 Desktop/CLI slice;将它拉入本 PR 会破坏审查边界,而不是修复缺陷。

  • Signed route record 的必要性: 现有 profile hints 是 operator 配置的可变提示;当所有已知地址过期时,Identify/DCUtR 无法在连接建立前完成 bootstrap。Coordination Relay 只有在已配置且可用时才能提供 bootstrap,而已批准的合同也支持完全关闭 Relay。Signed cache 不是另一套 target 或 identity authority:它只提供可变 routes,connector 最终仍固定并验证请求的 PeerId。三节点确定性传播测试与 installed native wrong-hint 连接已经证明本层不变量;完整 Desktop/Linux 地址变化流程属于下一条产品 slice。

  • Authenticated equal-sequence merge: 我实际尝试了建议的通用 merge 路径,它会破坏一个已有的高价值恢复场景:invitation redemption 已提交但响应丢失时,尚未完成 join 的 peer 没有 durable membership route,只能使用相同初始 sequence 重新签署新的短期 route。Authority 已认证同一个 PeerId 并验证其签名,因此这个窄的 redemption 路径允许替换该事实;现有 live route 的更低 sequence 仍会被拒绝。Sync/gossip 继续保持严格。两条路径必须不同,才能保证已提交 redemption 的幂等恢复。

  • Active application stream 错误: 每个 PeerId 只允许一个 active application stream 是本 PR 之前就存在的约束。本 PR 的 TypeScript queue 只负责串行化 application 与 Mesh control 的并发 dial;它不能把一个已经被占用的 framed application stream 交给另一个 caller。未来如果产品证据表明确实需要更清晰的重复连接诊断,应由 connection owner 去重或增加独立错误码处理,而不是改变本次 Mesh discovery slice。

@jackwener jackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at exact head 8bc4f7bb31babc27326970871bf898f2084ea929.

The long reconciliation-round defect is closed. The node now refreshes and re-reads its signed local route before every synchronization page, so earlier unreachable peers cannot consume the proof lifetime before a later healthy member is reached. I removed that refresh in a local mutation: the new 14-peer regression failed with the healthy member stuck at roster revision 13 instead of advancing to 14; restoring the production line made it pass.

I also re-evaluated the earlier scope and necessity concerns. This pull request is an explicit discovery/recovery library layer with Desktop and CLI management deferred to the next stacked slice. Its installed-package smoke exercises the single shared endpoint composition without creating a second State Root or identity authority. The signed route cache also has a concrete role when relays are disabled and every persisted route hint is stale: another authenticated member can carry a fresh route, while the connector still pins and verifies the requested Peer ID. Product startup wiring and the complete Desktop address-change journey remain acceptance work for the later product slice, not missing behavior claimed by this one.

No P0–P2 findings remain on this head. The full affected build passed, the new route-refresh regression passed and failed under the targeted mutation above, all applicable exact-head hosted checks are complete and successful, and a synthetic merge with current main (4b202615f1b2aec5018dfa52cdc4650c4a9ec8e9) builds cleanly.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

@M4n5ter
M4n5ter merged commit 6aa8f38 into main Aug 28, 2026
15 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-peer-mesh-discovery branch August 28, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XL Over 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants