How the iii-bridge Worker Enables Remote Worker Connections and Function Forwarding

The iii-bridge worker acts as a persistent WebSocket adapter that registers proxy functions and triggers on a remote iii engine, forwarding all local function calls, queue operations, and state manipulations transparently through III::trigger calls.

The iii-bridge worker in the iii-hq/iii repository provides a thin adapter layer that allows local engine instances to connect with remote workers using the iii WebSocket protocol. By implementing the BridgeAdapter pattern found in engine/src/workers/queue/adapters/bridge.rs, the system bridges network boundaries to make remote workers appear as native components of the local engine.

Step 1: Initializing the Persistent WebSocket Connection

The connection begins in BridgeAdapter::new, which constructs an III client using register_worker(&bridge_url, InitOptions::default()). This establishes a persistent WebSocket connection to the bridge server, defaulting to ws://localhost:49134 if no URL is specified in the configuration.

The client maintains this connection throughout the adapter's lifecycle, enabling bidirectional communication between the local and remote engines. According to the source in engine/src/workers/queue/adapters/bridge.rs (lines 32-38), this initialization is the foundation for all subsequent remote operations.

Step 2: Registering Proxy Functions on the Remote Side

Once connected, the adapter registers handler functions on the remote bridge to intercept incoming requests. Using bridge.register_function(handler_path, RegisterFunction::new_async(...)), the adapter creates a proxy that forwards payloads to the local engine via engine.call(&function_id, data).await.

As implemented in engine/src/workers/queue/adapters/bridge.rs (lines 82-86), these handlers act as the entry point for remote engines to execute functions on the local worker. The registration uses asynchronous closures to handle incoming JSON payloads and return results back across the WebSocket connection.

Step 3: Configuring Bridge Triggers for Queue Operations

The adapter registers bridge triggers using bridge.register_trigger to map queue topics to the previously registered handlers. This step, found at lines 61-68 of the queue adapter, tells the remote bridge which function to invoke when specific queue events occur.

When the local engine needs to enqueue a message or publish to a queue, the adapter builds a TriggerRequest and calls bridge.trigger(TriggerRequest { function_id: "...", payload, ... }). The bridge server receives this request, looks up the registered function ID from Step 2, and executes it on the remote side.

Step 4: Forwarding State Operations and Outbound Calls

For state manipulation (CRUD operations), the bridge worker follows an identical pattern in engine/src/workers/state/adapters/bridge.rs. The adapter forwards set, get, update, and delete operations by constructing trigger requests that target function IDs like "state::set" on the remote engine.

For example, when setting a state value, the adapter triggers a request with a JSON payload containing the scope, key, and value. The remote engine processes this request against its own state store and returns the result, which the adapter deserializes into SetResult or UpdateResult types.

Step 5: Deserializing Remote Responses and Error Handling

Responses from the remote engine arrive as JSON payloads through the WebSocket connection. The bridge adapter unmarshals these into appropriate Rust types such as UpdateResult, SetResult, or IIIError. Error conditions are wrapped and bubbled up as IIIError instances, maintaining type safety across the network boundary.

This deserialization occurs in the various adapter implementations—queue, state, and configuration—ensuring that remote errors appear as native exceptions to the local engine.

Configuration Setup for Remote Connections

The bridge URL is specified in the engine's config.yaml under the bridge_url key. The adapters read this configuration using config.get("bridge_url") and default to ws://localhost:49134 when not specified, as shown in lines 71-77 of the queue adapter.

modules:
  - name: workers::queue::QueueModule
    config:
      adapter:
        name: workers::queue::adapters::Bridge
        config:
          bridge_url: "ws://remote-host:49134"

When the engine initializes with this configuration, it invokes BridgeAdapter::new with the specified URL, establishing the remote connection before processing any worker tasks.

Practical Implementation Examples

Enqueueing Messages via the Bridge

To publish a message through the bridge connection:

use iii_sdk::{III, TriggerRequest, TriggerAction};
use serde_json::json;

async fn publish_via_bridge(bridge: &III, topic: &str, data: serde_json::Value) {
    bridge
        .trigger(TriggerRequest {
            function_id: "iii::durable::publish".to_string(),
            payload: json!({ "topic": topic, "data": data }),
            action: Some(TriggerAction::Void),
            timeout_ms: None,
        })
        .await
        .expect("bridge publish failed");
}

Registering Remote Functions

To register a function that remote engines can invoke through the bridge:

use iii_sdk::{RegisterFunction, III};

fn register_echo(bridge: &III) {
    let b = bridge.clone();
    bridge.register_function(
        "my::echo",
        RegisterFunction::new_async(move |payload: serde_json::Value| {
            let b = b.clone();
            async move { Ok(payload) }
        }),
    );
}

Forwarding State Set Operations

The state adapter implements forwarding like this:

use iii_sdk::TriggerRequest;
use serde_json::json;

// Inside BridgeAdapter::set()
let result = self.bridge.trigger(TriggerRequest {
    function_id: "state::set".to_string(),
    payload: json!({ "scope": scope, "key": key, "value": value }),
    action: None,
    timeout_ms: None,
}).await?;

Summary

  • The iii-bridge worker uses BridgeAdapter to maintain persistent WebSocket connections between local and remote iii engines.
  • Remote functions are registered via bridge.register_function, creating proxy handlers that forward calls to engine.call.
  • Queue operations and state manipulations use bridge.trigger with TriggerRequest payloads to execute functions remotely.
  • Configuration occurs through config.yaml using the bridge_url parameter, defaulting to ws://localhost:49134.
  • Response deserialization and error handling occur in engine/src/workers/queue/adapters/bridge.rs and engine/src/workers/state/adapters/bridge.rs.

Frequently Asked Questions

How does the iii-bridge worker handle connection failures?

The iii-bridge worker maintains a persistent WebSocket client that attempts to keep the connection alive. If the connection drops, the underlying III client handles reconnection logic according to the InitOptions specified during register_worker. Errors during trigger calls are wrapped as IIIError and returned to the caller, allowing the local engine to implement retry logic or circuit breakers as needed.

What is the performance overhead of using iii-bridge for function forwarding?

The bridge introduces latency equivalent to one network round-trip plus JSON serialization/deserialization overhead. All calls are asynchronous (new_async), allowing the local engine to continue processing while waiting for remote responses. For high-throughput scenarios, the persistent WebSocket connection avoids TCP handshake overhead for subsequent requests after the initial connection.

Can the iii-bridge worker forward arbitrary function calls or only specific operations?

The bridge can forward any function call registered via register_function. The implementation in console/packages/console-rust/src/bridge/functions.rs demonstrates registering console-specific functions (engine::console::*), but the pattern applies to any custom function. Both queue operations (publish/enqueue) and state operations (get/set/delete) use the same underlying bridge.trigger mechanism.

Where is the bridge URL configured in a production deployment?

The bridge URL is configured in the engine's YAML configuration file under the bridge_url key within the adapter configuration section. As shown in sdk/fixtures/config-bridge.yaml, production deployments typically specify ws://remote-host:49134 or the appropriate remote address. The default value of ws://localhost:49134 is only suitable for local development.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →