Reaktor Mesh

Mesh is the live-channel part of the Data layer. Durable jobs, recordings and processing handoffs follow Queues and Workflows; a live socket does not provide durable intent or recovery.

Data-layer alignment (11 September 2026). This module participates in the unified data infrastructure through the same Service contracts and main graph identities. Delivery/session mechanisms retain their own guarantees; operation, publication, commit, projection and device-delivery receipts are distinct. The generalized Service interfaces are proposed, not implied to ship with this module.

Reaktor Mesh is the live connectivity layer for Reaktor.

It is the place for peer sessions, realtime data channels, media tracks, presence, WebRTC negotiation, TURN credential issuance, SFU routing, and graph-visible realtime topology.

It is not the durable bus. It does not own replay, outbox, DLQ, or long-lived event retention. When a realtime interaction must become an auditable business fact, the mesh hands that fact to Reaktor Bus or a typed service.

1. Why Mesh Exists

Many product features need live links, not durable queues:

  • calls;
  • screenshare;
  • collaborative cursors;
  • low-latency game state;
  • presence;
  • nearby or peer device sync;
  • live debugging sessions;
  • control-plane streams;
  • realtime media processing;
  • temporary peer-to-peer data movement.

These interactions have different constraints from bus messages:

  • low latency matters more than replay;
  • session lifecycle matters more than retention;
  • network topology is part of the product behavior;
  • backpressure and disconnect are normal;
  • credentials must be short-lived;
  • not every packet should become a database row.

Reaktor Mesh gives those links the same architectural treatment as services, data, and bus routes: typed contracts, graph nodes, explicit policy, and inspectable runtime state.

2. Boundaries

SystemOwnsDoes not own
reaktor-serviceRequest/response operations and endpoint identity.Long-lived peer session topology.
Reaktor BusDurable events/jobs, queues, Pub/Sub, outbox, DLQ, replay.Live media/data transport.
reaktor-workScheduled/background execution.Realtime session routing.
Reaktor MeshWebRTC, TURN, SFU sessions/tracks, DataChannels, presence, ICE state, peer topology.Durable event history or background job ownership.
Distributed actorsServer-side placement, mailboxes, supervision, actor remoting.Browser/mobile WebRTC session lifecycle.

Practical rule:

If it must survive disconnect and be replayed, use Bus or a service write. If it must be live, low-latency, and session-scoped, use Mesh.

3. Provider Roles

Reaktor will use three Cloudflare realtime capabilities differently.

Provider capabilityReaktor roleNotes
Cloudflare TURNConnectivity fallback and ICE server credential issuance.TURN keys stay server-side. Clients receive short-lived credentials only.
Cloudflare Realtime SFUServerless WebRTC sessions, media tracks, and global track routing.Good for calls, broadcast, media rooms, and session-based data transport.
Cloudflare Realtime DataChannelsLow-latency arbitrary data to many subscribers.Good for cursor state, game state, control messages, and collaboration.

Mesh may also use:

  • direct WebRTC between peers when allowed by network policy;
  • Durable Objects for room/session directory and authorization;
  • Worker service bindings for credential issuance and privileged calls;
  • Bus for durable facts that should outlive a session.

4. TURN Credential Model

TURN credentials are security-sensitive.

Cloudflare TURN keys must never be embedded in a browser, mobile app, desktop app, or public config. The application backend uses the TURN key to mint short-lived credentials, and the client uses only those generated ICE server credentials.

The Reaktor shape:

interface MeshCredentialService {
    suspend fun issueIceServers(scope: MeshCredentialScope): IceServerBundle
}

data class MeshCredentialScope(
    val tenantId: String?,
    val principalId: String,
    val sessionId: String,
    val allowedTransports: Set<MeshTransport>,
    val ttlSeconds: Int,
)

data class IceServerBundle(
    val iceServers: List<IceServer>,
    val expiresAtEpochMillis: Long,
)

data class IceServer(
    val urls: List<String>,
    val username: String? = null,
    val credential: String? = null,
)

Rules:

  • TURN key is stored only in a server-side secret store.
  • Credentials are scoped to the authenticated user/session.
  • Credentials have short TTLs.
  • The backend returns the provider iceServers shape the WebRTC client can pass to RTCPeerConnection.
  • Browser clients should not be handed TURN URLs that browsers cannot use reliably.
  • Credential issuance is audited as a service operation and projected into the graph.

This replaces product-specific realtime token endpoints with a framework surface that can support chat, calls, collaborative tools, and control-plane sessions.

5. Cloudflare Realtime SFU Model

Cloudflare Realtime SFU gives Reaktor a serverless WebRTC media/data plane.

Provider concepts map cleanly into Reaktor graph concepts:

Cloudflare conceptReaktor concept
ApplicationMeshApplicationNode
SessionMeshSessionNode; maps to one WebRTC PeerConnection to Cloudflare.
TrackMeshTrackNode; maps to a MediaStreamTrack and has a globally usable id.
DataChannelMeshDataChannelNode; low-latency arbitrary data path.
Track subscriptionMeshTrackSubscriptionEdge.
Anycast locationMeshRegionNode or runtime attribute on session.

Key provider facts to encode:

  • A Session maps to a WebRTC PeerConnection.
  • Cloudflare anycast connects the client to a nearby data center.
  • Tracks map to WebRTC MediaStreamTrack objects.
  • Tracks have global ids and can be made available to other sessions.
  • API calls are scoped per session and limited to 50 calls per second per session.
  • A single API call can include up to 64 tracks.
  • Tracks are garbage-collected after 30 seconds of inactivity.
  • PeerConnection must be connected; some operations wait up to 5 seconds for connection.
  • Applications should implement reconnection strategy.
  • Client-to-Cloudflare media/data ingress is free; Cloudflare-to-client egress has a free monthly allotment and then billing.

The adapter must surface these facts in configuration and telemetry. A graph that hides session churn, track count, or reconnect loops is not useful for operating realtime systems.

6. DataChannels

DataChannels carry arbitrary low-latency data through the realtime session.

Use DataChannels for:

  • collaborative cursors;
  • editor awareness;
  • transient game state;
  • call controls;
  • realtime debug/control messages;
  • room-local presence updates;
  • mesh control-plane heartbeats.

Do not use DataChannels for:

  • durable business events;
  • large file transfer without a separate reliability protocol;
  • payment/order/inventory state;
  • anything that needs DLQ or replay.

Cloudflare Realtime DataChannels can scale one producer to many subscribers. The provider also has a waitForAck option that helps prevent the first message from being lost; when enabled, the subscriber must acknowledge within 15 seconds.

Reaktor should expose the reliability mode explicitly:

enum class MeshChannelDurability {
    Ephemeral,
    AckBeforeFirstMessage,
    MirrorToBus,
}

data class MeshDataChannelOptions(
    val label: String,
    val ordered: Boolean = false,
    val maxPacketLifeTimeMillis: Int? = null,
    val durability: MeshChannelDurability = MeshChannelDurability.Ephemeral,
    val mirrorKind: String? = null,
)

MirrorToBus does not make the DataChannel durable. It means the application also publishes selected semantic messages to Reaktor Bus.

7. Core API

The mesh API should be session-first.

interface ReaktorMesh {
    suspend fun issueIceServers(scope: MeshCredentialScope): IceServerBundle

    suspend fun openSession(
        identity: MeshIdentity,
        options: MeshSessionOptions = MeshSessionOptions(),
    ): MeshSession

    suspend fun publishTrack(
        sessionId: String,
        track: MeshTrackDescriptor,
    ): MeshTrackRef

    suspend fun subscribeTrack(
        sessionId: String,
        trackId: String,
        options: MeshTrackSubscribeOptions = MeshTrackSubscribeOptions(),
    ): MeshSubscription

    suspend fun openDataChannel(
        sessionId: String,
        options: MeshDataChannelOptions,
    ): MeshDataChannel

    suspend fun closeSession(sessionId: String, reason: String? = null)
}

data class MeshIdentity(
    val tenantId: String?,
    val principalId: String,
    val deviceId: String?,
    val graphNodeId: String,
)

data class MeshSessionOptions(
    val roomId: String? = null,
    val preferredRegion: String? = null,
    val requireTurn: Boolean = false,
    val allowDirectPeer: Boolean = true,
    val allowSfu: Boolean = true,
)

The API should not expose provider-specific objects at the application boundary. Provider details live in adapters and diagnostics.

8. Session Lifecycle

A mesh session has a lifecycle distinct from component lifecycle and bus delivery lifecycle.

Requested
  -> CredentialIssued
  -> Signaling
  -> Connecting
  -> Connected
  -> Degraded
  -> Reconnecting
  -> Closed

Events:

EventMeaning
onCredentialIssuedICE/TURN bundle created.
onSignalOfferLocal offer created or received.
onSignalAnswerRemote answer created or received.
onIceCandidateICE candidate observed.
onConnectedPeerConnection or provider session connected.
onTrackPublishedLocal track became available.
onTrackSubscribedRemote track subscription attached.
onDataChannelOpenDataChannel ready for messages.
onDegradedRTT, packet loss, reconnect, or provider error crossed threshold.
onReconnectingSession attempting recovery.
onClosedSession closed intentionally or permanently failed.

The lifecycle should be graph-native: a connected session changes graph state, not just a callback in UI code.

9. Signaling And Directory

Mesh needs a signaling and directory plane, but it should stay separate from the media/data plane.

Responsibilities:

  • authenticate session requests;
  • authorize room/session access;
  • issue TURN credentials;
  • create or join provider sessions;
  • exchange offers, answers, and ICE candidates when direct WebRTC is used;
  • map product room ids to mesh session ids;
  • expose presence and connection state;
  • write durable facts to Bus when required.

Good first substrate:

  • Cloudflare Worker route for credential/session APIs;
  • Durable Object per room for directory, presence, and signaling;
  • Cloudflare Realtime SFU for media/data when direct peer paths are not enough;
  • Reaktor Bus for durable facts like Call.Started, Call.Ended, Presence.Changed, or Mesh.SessionFailed.

10. Graph Projection

Mesh is graph-visible. These nodes reach a workbench over Reaktor Graph Protocol, like every other runtime's graph.

Graph nodeMeaning
MeshNodeLogical mesh subsystem for an app/service.
MeshRoomNodeProduct room/call/collaboration space.
MeshSessionNodeOne live peer/provider session.
PeerNodeUser/device/service participant.
TurnCredentialNodeShort-lived ICE credential issuance record.
SfuNodeProvider SFU application/session resource.
MeshTrackNodeAudio/video/screen/data media track.
MeshDataChannelNodeLow-latency data channel.
RelayNodeTURN/SFU relay path.
MeshMetricNodeRTT, packet loss, bitrate, reconnect count, jitter, error budget.

Edges:

  • issuesCredential;
  • joinsRoom;
  • connectsTo;
  • publishesTrack;
  • subscribesTrack;
  • opensDataChannel;
  • relaysThrough;
  • mirrorsToBus;
  • signalsThrough;
  • degrades;
  • disconnects.

Workbench overlays should show:

  • active sessions by room;
  • ICE state;
  • direct vs TURN vs SFU path;
  • RTT and packet loss;
  • reconnect loops;
  • media track bitrate and resolution;
  • DataChannel message rate;
  • sessions with expired or near-expired credentials;
  • mesh messages mirrored into Bus.

11. Mesh To Bus Handoff

Mesh messages are ephemeral by default. Some effects must be durable.

Examples:

Live mesh actionDurable bus/service fact
User joins a callCall.ParticipantJoined
User leaves a callCall.ParticipantLeft
Screen share startsCall.ScreenShareStarted
Collaboration patch acceptedDocument.PatchCommitted
Session fails after retriesMesh.SessionFailed
Presence changesPresence.Changed with coalescing policy

Rules:

  • Do not mirror every packet.
  • Mirror semantic facts.
  • Coalesce noisy presence/awareness updates.
  • Write through a service when the fact changes authoritative state.
  • Publish to Bus when the fact fans out or triggers work.

12. Security

Mesh is a security boundary.

Required controls:

  • authenticate every credential/session request;
  • authorize access to the room/session/track;
  • keep TURN keys server-side;
  • issue short-lived ICE credentials;
  • bind credentials to principal, tenant, session, and device when possible;
  • validate track subscription rights;
  • avoid logging raw SDP, tokens, or ICE credentials;
  • redact mesh metadata in graph exports when needed;
  • rate-limit credential issuance and session APIs;
  • treat DataChannel messages as untrusted input;
  • mirror durable facts through normal service/bus authorization.

DataChannels are not a trust shortcut. A peer that can send a realtime packet has not earned permission to mutate durable state.

13. Failure Semantics

Mesh failure is expected.

Failure modes:

  • TURN credential expired;
  • ICE gathering failed;
  • direct peer path blocked;
  • TURN relay required;
  • SFU session not connected;
  • track inactive and garbage-collected;
  • DataChannel first message lost without ack;
  • subscriber joined after ephemeral message was sent;
  • mobile app paused or backgrounded;
  • network switched from Wi-Fi to cellular;
  • room Durable Object restarted;
  • provider rate limit hit.

Reaktor must surface these as typed states, not unstructured console logs.

Policy examples:

FailureDefault policy
Direct path failsRetry through TURN or SFU.
Credential expiredRe-issue if auth/session still valid.
DataChannel first message importantUse AckBeforeFirstMessage or mirror semantic fact to Bus.
Track inactiveRe-publish or close subscription after grace period.
Reconnect loopMark session Degraded, emit telemetry, optionally publish Mesh.SessionFailed.
Provider rate limitBack off and surface graph warning.

14. Implementation Plan

Build Mesh in layers:

  1. reaktor-mesh-core: identity, credential scope, session model, lifecycle events, graph nodes.
  2. reaktor-mesh-webrtc: common WebRTC client abstractions for browser/mobile/desktop.
  3. reaktor-mesh-cloudflare-turn: server-side TURN credential adapter.
  4. reaktor-mesh-cloudflare-realtime: SFU sessions, tracks, subscriptions, DataChannels.
  5. reaktor-mesh-signaling: Durable Object/Worker signaling and room directory helpers.
  6. reaktor-mesh-bus: semantic event mirror and mesh-to-bus policy.
  7. reaktor-mesh-graph: workbench projection and overlays.
  8. Product migration: replace BestBuds-specific realtime token and room state with mesh primitives.

Keep ServiceTransport.PEER as endpoint identity. The runtime implementation belongs in Mesh.

15. Acceptance Tests

A real implementation is not done until these pass:

  • TURN credentials are issued only by authenticated server-side code;
  • TURN key never appears in client bundles or logs;
  • issued ICE credentials have bounded TTL and include expected iceServers;
  • unauthorized principal cannot join a mesh room;
  • session lifecycle emits graph events in order;
  • direct peer path can fall back to TURN/SFU;
  • SFU session creates and publishes a track descriptor;
  • another session can subscribe to the track;
  • DataChannel AckBeforeFirstMessage waits for subscriber acknowledgement;
  • DataChannel ephemeral messages are not treated as durable facts;
  • MirrorToBus publishes only semantic messages;
  • graph export shows peers, sessions, TURN/SFU path, tracks, channels, metrics, and bus mirror edges;
  • reconnect loop becomes a typed degraded state and telemetry span;
  • provider limits are represented as adapter constants and covered by tests.

16. Provider References

Use current provider docs while implementing adapters: