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

> Discover how the iii-bridge worker connects remote workers and forwards functions transparently. Learn about its persistent WebSocket adapter and trigger capabilities.

- Repository: [iii/iii](https://github.com/iii-hq/iii)
- Tags: how-to-guide
- Published: 2026-05-28

---

**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](https://github.com/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`](https://github.com/iii-hq/iii/blob/main/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`](https://github.com/iii-hq/iii/blob/main/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`](https://github.com/iii-hq/iii/blob/main/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`](https://github.com/iii-hq/iii/blob/main/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`](https://github.com/iii-hq/iii/blob/main/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.

```yaml
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:

```rust
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:

```rust
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:

```rust
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`](https://github.com/iii-hq/iii/blob/main/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`](https://github.com/iii-hq/iii/blob/main/engine/src/workers/queue/adapters/bridge.rs) and [`engine/src/workers/state/adapters/bridge.rs`](https://github.com/iii-hq/iii/blob/main/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`](https://github.com/iii-hq/iii/blob/main/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`](https://github.com/iii-hq/iii/blob/main/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.