Synapse Framework
Themes Plugins Docs Home Github
Github - Releases Join Discord

Executor bridge — Port, Stream, and Compat

Synapse Framework connects the desktop UI to a Roblox executor through a local HTTP bridge on 127.0.0.1:31337. Three transport methods share one Axum server; Settings chooses which queue and Lua client the UI uses.

Settings labelbridgeMethodLua clientRust module
FastestportPort Bridge.luasrc-tauri/src/port_bridge.rs
StreamstreamStream Bridge.luasrc-tauri/src/stream_bridge.rs
CompatiblecompatCompat Bridge.luasrc-tauri/src/compat_bridge.rs

Legacy WebSocket (/ws, Websocket Bridge.lua) has been removed. Saved settings with bridgeMethod: "websocket" are migrated to port automatically.


Overview

flowchart LR
  UI["Synapse UI"] -->|bridge_send_execute| Rust["bridge.rs"]
  Rust --> PortQ["port_pending_execs"]
  Rust --> StreamQ["stream_pending_execs"]
  Rust --> CompatQ["compat_pending_execs"]
  PortQ --> PortLua["Port Bridge.lua"]
  StreamQ --> StreamLua["Stream Bridge.lua"]
  CompatQ --> CompatLua["Compat Bridge.lua"]
  PortLua <-->|HTTP :31337| Axum["Axum server"]
  StreamLua <-->|HTTP :31337| Axum
  CompatLua <-->|HTTP :31337| Axum
  • Server: Axum on http://127.0.0.1:31337 (spawned from src-tauri/src/lib.rs).
  • Setting: bridgeMethod in src/app/appSettings.ts — available in every UI shell’s Settings/Options.
  • Scripts: Bundled in src-tauri/resources/scripts/ and src/assets/editor-sidebar-scripts/ (synced via npm run sync:bridge-scripts).

Attach flow

  1. Click Attach in the Synapse UI.
  2. Run the matching bridge script in your executor:
    • Sidebar: Port Bridge.lua, Stream Bridge.lua, or Compat Bridge.lua
    • Or download from the local server (see below).
  3. The UI shows Connected when the Rust side reports *_connected: true for the selected method.

Script download URLs

URLFile served
GET http://127.0.0.1:31337/port_bridge.luaPort Bridge.lua
GET http://127.0.0.1:31337/stream_bridge.luaStream Bridge.lua
GET http://127.0.0.1:31337/compat_bridge.luaCompat Bridge.lua
GET http://127.0.0.1:31337/bridge.luaPort Bridge.lua (alias)
GET http://127.0.0.1:31337/healthok

Execute flow (UI → executor)

  1. User clicks Execute (or auto-execute runs).
  2. Frontend calls Tauri bridge_send_execute with { source, method } where method is port, stream, or compat.
  3. Rust enqueues the script (base64) on the method-specific pending queue.
  4. The Lua client polls */next, receives the payload, runs it in the game, and posts */result.
  5. UI receives synapse:bridge-execute-result; errors and logs appear in the F9 console via synapse:bridge-log.

Central dispatch lives in src/app/executorBridge/bridgeDispatch.ts. Shells use useBridgeExecuteReady() for execute enable/disable.

Status and events

IPC / eventPurpose
bridge_statusSnapshot: port_*, stream_*, compat_*, legacy_disabled
synapse:bridge-statusPushed status updates to all webviews
synapse:bridge-execute-resultExecute outcome { id, ok, error? }
synapse:bridge-logF9 console lines from executor or plugins

legacy_disabled: true means a plugin bridge provider has taken over — Port/Stream/Compat execute is blocked in Rust until legacy is re-enabled.

Plugin bridge providers

Plugins with the bridge permission may register a bridge provider that replaces legacy execute when active:

  • host.setLegacyBridgeDisabled(true) sets legacy_disabled in Rust.
  • Only one provider owns execute at a time; legacy Port/Stream/Compat is unavailable while it owns the bridge.
  • Undocked V3 tabs and Script Hub webviews do not bootstrap plugins; they relay provider execute to the main window via dispatchBridgeExecuteViaMain when the provider owns the bridge (src/app/executorBridge/useBridgeExecuteReady.ts).

See PLUGINS.md for the generic bridge-provider API (no vendor-specific plugins documented here).


Port (Fastest) — default

Best for: Almost every executor — only requires game:HttpGet. Uses request() (or syn.request / http_request) for POST when available.

Base URL: http://127.0.0.1:31337/port_bridge

Endpoints

MethodPathPurpose
GET / POST/port_bridge/helloClient identification; sets port_connected
GET/port_bridge/pingLightweight liveness (no status emit)
GET/port_bridge/nextLong-poll (≤10s) for execute payload(s)
GET / POST/port_bridge/resultReturn execute result (error_b64 on GET for HttpGet-only)
GET / POST/port_bridge/logStream a log line to F9

Behavior

  • Long-poll on next: Server holds the GET up to ~10 seconds when idle; delivers executes immediately when queued. Steady-state polling acts as a heartbeat.
  • Liveness: Watchdog clears port_connected after ~45s without client traffic (3 consecutive stale ticks).
  • Batching: Up to 16 pending executes per next response when the queue is backed up.
  • HttpGet fallback: Result and log endpoints support GET with base64 query params when POST is unavailable.

When to use

  • Default choice for new setups.
  • Executors with weak or missing request() support.
  • Lowest executor requirements of the three methods.

Source: src-tauri/src/port_bridge.rs, src/assets/editor-sidebar-scripts/Port Bridge.lua


Stream

Best for: Executors with reliable request() / HTTP client support. Optional NDJSON heartbeat stream; execute delivery uses the same long-poll contract as Port.

Base URL: http://127.0.0.1:31337/stream_bridge

Endpoints

MethodPathPurpose
GET / POST/stream_bridge/helloClient identification; sets stream_connected
GET/stream_bridge/pingLightweight liveness
GET/stream_bridge/streamNDJSON heartbeat only (does not drain executes)
GET/stream_bridge/nextLong-poll (≤10s) for execute payload(s)
GET / POST/stream_bridge/resultExecute result
GET / POST/stream_bridge/logLog line

Behavior

  • /stream_bridge/stream: Keeps stream_connected alive with periodic NDJSON heartbeats (~15s). Does not deliver scripts — prevents stray push clients from stealing payloads without dispatching them.
  • Execute delivery: Same as Port — long-poll on /stream_bridge/next.
  • Liveness: ~45s idle timeout (same watchdog pattern as Port).
  • Batching: Up to 16 executes per next response.

When to use

  • Executor has request() and you want Stream’s heartbeat stream for connection stability.
  • Port works but you prefer the Stream client’s polling model.

Source: src-tauri/src/stream_bridge.rs, src/assets/editor-sidebar-scripts/Stream Bridge.lua


Compat (Compatible)

Best for: Executors that only support game:HttpGet and cannot hold long HTTP connections (long-poll fails or times out aggressively).

Base URL: http://127.0.0.1:31337/compat_bridge

Endpoints

MethodPathPurpose
GET / POST/compat_bridge/helloClient identification; sets compat_connected
GET/compat_bridge/pingLightweight liveness
GET/compat_bridge/nextImmediate response (exec or null) — no server-side long-poll
GET / POST/compat_bridge/resultExecute result (GET + base64 params)
GET / POST/compat_bridge/logLog line

Behavior

  • Short-poll: Client polls /compat_bridge/next every ~0.5s; server returns immediately.
  • All traffic via GET where needed (base64-encoded query parameters).
  • Liveness: Shorter idle timeout — ~15s without client traffic.
  • Batching: Up to 16 executes per next response when multiple are queued.

When to use

  • Port long-poll disconnects or never shows Connected.
  • Executor blocks or kills requests that stay open for several seconds.
  • HttpGet-only environment with no usable request().

Source: src-tauri/src/compat_bridge.rs, src/assets/editor-sidebar-scripts/Compat Bridge.lua


Choosing a method

SituationRecommended method
Not sure / first tryPort (Fastest)
Port disconnects on long HTTP holdsCompat
Strong request() support, want stream heartbeatStream
Plugin bridge provider activeLegacy methods disabled — use the provider

Switch methods in Settings → Bridge method. After switching, re-run the matching Lua script in your executor.

Undocked tabs and Script Hub

Secondary webviews (V3 undock, Script Hub) share bridge state via:

  • synapse:bridge-status events from Rust
  • synapse.bridgeSession.v1 in localStorage (published by the main window)
  • Legacy (Port/Stream/Compat): Execute calls bridge_send_execute directly — the bridge server is global, not per-window.
  • Plugin provider: Undock relays execute to main when legacy_disabled is true and the session reports the provider connected.

Key source files

ConcernPath
Router + execute dispatchsrc-tauri/src/bridge.rs
Port transportsrc-tauri/src/port_bridge.rs
Stream transportsrc-tauri/src/stream_bridge.rs
Compat transportsrc-tauri/src/compat_bridge.rs
Bridge settingssrc/app/appSettings.ts
UI bridge contextsrc/app/executorBridge/ExecutorBridgeContext.tsx
Central execute routingsrc/app/executorBridge/bridgeDispatch.ts
Connection displaysrc/app/executorBridge/bridgeConnectionDisplay.ts
Lua clientssrc/assets/editor-sidebar-scripts/*.lua
Script sync to Tauri resourcesscripts/sync-bridge-scripts.mjs

Internal legacy note

bridge.rs still exposes /matcha/* HTTP routes for an older internal HTTP client path. This is not a user-facing bridge method and is not selectable in Settings. Use Port, Stream, or Compat instead.