daemon: extend IPC with typed-channel + RPC + capabilities ops#16
Merged
Conversation
Adds the missing IPC surface so a client that's not embedding
myownmesh-core directly can still use the engine's typed channels
and RPC dispatcher — the parts of the library API that were
previously lib-only. This is the foundation for porting the
MyOwnLLM app off Trystero onto the daemon (separate PR over there
once this lands and the LLM bumps its git pin).
**New IPC ops** (added to `control::Request`, all additive — no
existing op changed):
- `RpcRegister { client_id, network, method, streaming }` —
claim a method name. The daemon installs a synthetic
`Rpc::serve` (or `serve_stream`) on the network's dispatcher
that emits `RpcInbound` to the claiming client's event socket
and awaits an `RpcRespond` / `RpcStreamChunk` / `RpcStreamEnd`
routed back through a per-request pending table. Last-claim-wins
with a `HandlerDisplaced` event to the displaced client.
- `RpcUnregister` — release a method claim.
- `RpcRespond` / `RpcStreamChunk` / `RpcStreamEnd` — client
resolves an in-flight inbound RPC.
- `RpcCall` — outbound single-shot; daemon awaits `Rpc::call`
and returns the response synchronously on the command socket.
- `RpcCallStream` — outbound streaming; daemon assigns a
request_id, spawns a forwarder that drains the engine's
`mpsc::Receiver` and emits `RpcCallStreamChunk` /
`RpcCallStreamEnd` events to the client's event socket.
- `ChannelSubscribe` / `ChannelUnsubscribe` — typed-channel
fan-out. First subscriber spawns a pump task; last unsubscribe
drops it.
- `ChannelSendTo` / `ChannelSendAll` — fire-and-forget channel
publishes.
- `CapabilitiesSet` — replace the network's advertised
capabilities at runtime.
**New wire-out variants** (`ipc::ServerOut`, written on the event
socket after `EventsSubscribe`):
- `RpcInbound` — peer called a method this client owns.
- `RpcCallStreamChunk` / `RpcCallStreamEnd` — chunks of an
outbound stream call this client initiated.
- `ChannelInbound` — frame on a subscribed channel.
- `HandlerDisplaced` — a more-recent client took your method.
**Architecture**: each event-subscribed connection gets a
`ClientId` echoed back in the `EventsSubscribe` ack. Subsequent
RPC/channel-management ops on other command sockets pass that
`client_id` so the daemon routes inbound events to the right
event socket. Per-request `oneshot::Sender` / `mpsc::Sender`
entries live in `ClientRegistry::pending_inbound` keyed only by
`request_id` — any client may answer any id, which keeps
streaming responses decoupled from a particular connection if it
bounces.
**Tests** (13 new): unit tests for the registry mechanics
(claim/release/displace/subscribe/unsubscribe, pending-inbound
resolve/reject, stream chunk/end) plus three end-to-end bridge
tests that build two engines connected via LocalBroker and
validate the full round-trip for single-shot RPC, streaming RPC,
and channel pub/sub through the synthetic-handler bridge.
https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
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.
Summary
Foundation for porting MyOwnLLM off Trystero onto the daemon. The existing IPC was complete for control-plane (networks, peers, roster, governance, events) but typed channels and RPC were library-only — clients had to embed
myownmesh-coredirectly to use them, which made the LLM app's "use the daemon for all mesh" goal impossible. This PR adds the missing IPC surface so multiple clients (the existing GUI, the future LLM Tauri backend) can share one daemon, one identity, one set of networks.Once this merges, MyOwnLLM bumps its
myownmesh-coregit pin and a follow-up PR there rewritesmesh-client.svelte.tsto talk to the daemon — no second Mesh instance, no duplicate Trystero patches, both apps live on the same RPC/channel/governance plumbing.New IPC ops
All additive on
control::Request— no breaking changes to existing variants:rpc_register { client_id, network, method, streaming }Rpc::serve/serve_streamon the network's dispatcher that emitsRpcInboundto the claiming client and awaits the response via the registry'spending_inboundtable. Last-claim-wins.rpc_unregisterrpc_respond/rpc_stream_chunk/rpc_stream_endrpc_callrpc_call_streamrequest_idand forwards chunks as events.channel_subscribe/channel_unsubscribechannel_send_to/channel_send_allcapabilities_setNew event-stream variants
ipc::ServerOut, written on the event socket afterEventsSubscribe. Existing clients (the GUI) ignore unknownkinds by default, so this is additive on the wire.rpc_inbound— peer called a method this client ownsrpc_call_stream_chunk/rpc_call_stream_end— chunks of an outbound stream this client initiatedchannel_inbound— frame on a subscribed channelhandler_displaced— a more-recent client took your methodArchitecture
ClientId(monotonic,c<n>on the wire) echoed back in theEventsSubscribeack.client_idso the daemon routes inbound events to the right event socket.oneshot::Sender/mpsc::Senderentries live inClientRegistry::pending_inboundkeyed only byrequest_id— any client may answer any id, which keeps streaming responses decoupled from a particular connection if it bounces.Rpcdispatcher forever once a method is first claimed; if invoked with no current owner they answer the peer with "no handler" rather than panicking. Simpler than safe teardown across re-claims.Tests
13 new tests, all passing alongside the existing suite (no regressions):
ipc::clients::tests— registry mechanics (claim/release/displace, subscribe/unsubscribe with first/last subscriber flags, pending-inbound resolve/reject, stream chunk/end, ClientId roundtrip, monotonic id allocation, disconnect-cleanup that doesn't collateral-drop a displacing claim).ipc::bridge::tests— spin up two engines +LocalBroker, wait forPeerEvent::Approved, then validate the full round-trip:echo, Bob calls it, the simulated client receivesRpcInbound+ responds, Bob'srpc.call()future resolves with the payload.call_streamreceiver yields all three plus end-of-stream.catalog, Bob sends a frame, theChannelInboundevent arrives with correct sender + payload.Files
Test plan
cargo fmt --all --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace(147 tests total, all pass — 13 new, 134 pre-existing untouched)myownmesh-coregit pin and starts the Tauri-backend migration in its own PRhttps://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
Generated by Claude Code