# bridge

> Bridge two iii engines — expose local functions on a remote engine and invoke/forward remote functions locally.

| field | value |
|-------|-------|
| version | 0.1.8 |
| type | binary |
| license | Apache-2.0 |
| repo | https://github.com/iii-hq/workers |
| supported_targets | x86_64-apple-darwin, aarch64-apple-darwin, i686-pc-windows-msvc, x86_64-pc-windows-msvc, aarch64-pc-windows-msvc, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-unknown-linux-musl, armv7-unknown-linux-gnueabihf |
| author | iii |

## installation

```sh
iii trigger compose::add worker=bridge@0.1.8
```

## configuration

```yaml
- expose:

  forward:

  url: ws://0.0.0.0:49134
```

## dependencies

- `configuration` @ `0.x`

## readme

# bridge

Connects a local iii engine to a remote iii instance over a long-lived
`iii-sdk` WebSocket connection so functions on either side can call across the
boundary. `expose` entries make a local function callable *from* the remote
engine; `forward`/`invoke` entries make a remote function callable *from* this
engine. There are no trigger types.

## Install

```bash
iii trigger compose::add worker=bridge
```

`iii trigger compose::add` resolves the worker and its dependencies, writes
exact declarations to `worker-compose.yaml`, and reconciles the Compose project.

## Function surface

| Function | Input | Output |
|---|---|---|
| `bridge.invoke` | `{ function_id, data?, timeout_ms? }` | remote function's return value, waits up to `timeout_ms` (default `30000`) |
| `bridge.invoke_async` | `{ function_id, data?, timeout_ms? }` | `null`, immediately — fire-and-forget; `timeout_ms` is ignored |
| forward entry (`local_function`) | whatever the remote `remote_function` expects | remote function's return value, waits up to the entry's `timeout_ms` (default `30000`) |
| expose entry (registered on the remote, name defaults to `local_function`) | whatever the local `local_function` expects | local function's return value; a local failure's real `code`/`message`/`stacktrace` is forwarded untouched |

`bridge.invoke`/`bridge.invoke_async`/forward calls collapse any remote-side
failure to `code: "bridge_error"` — the underlying remote error code is never
surfaced through those three paths. A malformed `bridge.invoke*` input
(missing `function_id`) fails with `code: "deserialization_error"` instead.

## Configuration

Configuration is owned by the `configuration` worker — edit it from the
console (**Configuration → Workers → bridge**) or seed it once via
`--config <file>.yaml` on first boot:

The worker uses two independent WebSocket connections:

| Connection | URL selection | Purpose |
|---|---|---|
| Local/control engine | `--url` → `III_URL` → `ws://127.0.0.1:49134` | Register the `bridge` worker and receive local invocations. |
| Remote target | `config.url` | Forward invocations to the other iii engine. |

| Field | Default | Description |
|---|---|---|
| `url` | `ws://0.0.0.0:49134` | Remote target WebSocket URL. Set it explicitly when `III_URL` selects a non-default local/control engine. |
| `expose[]` | `[]` | `{ local_function, remote_function? }` — local functions the remote engine may call; `remote_function` is the name registered on the remote (defaults to `local_function`). |
| `forward[]` | `[]` | `{ local_function, remote_function, timeout_ms? }` — local aliases that proxy outbound to a remote function; `timeout_ms` overrides the per-call default (`30000`). |

Hot-reload semantics: a `url` change connects a new remote client, re-registers
every `expose` entry on it, then swaps it in and gracefully shuts the old
client down — no restart needed. An unchanged `url` with new `expose`/`forward`
entries registers just the additions live. **Removing** a `forward`/`expose`
entry does not un-register its handler — the SDK has no unregister — so the
function id stays live but its handler returns `bridge_error` until the worker
is restarted.

For backward compatibility, a missing remote `config.url` still falls back to
`III_URL`, then `ws://0.0.0.0:49134`. This fallback predates `III_URL` support
for the local/control connection. In Compose or other supervised deployments,
always set `config.url`; otherwise both connections can point to the local
engine and create a self-bridge.

## Requires removing the legacy built-in bridge worker

The legacy built-in bridge worker registers the same function ids
(`bridge.invoke`, `bridge.invoke_async`, plus any configured forward/expose
names). Two workers registering the same function id on one engine collide —
whichever registers last wins — so this worker requires the legacy built-in to be
absent: omit it from the engine's `config.yaml` (a config that doesn't list a
worker won't run it). This is a duplicate-function-id collision, not a trigger
type conflict — `bridge` registers no trigger types.

On boot, this worker queries the engine for connected workers and refuses to
start with a clear error if the legacy built-in is still active, so a stale config
fails loudly instead of silently racing the built-in worker for ownership of
`bridge.invoke`/`bridge.invoke_async`.

The engine's own state/queue/stream/configuration **bridge adapters** are
unaffected by this worker or by removing the legacy built-in: those adapters open
their own direct SDK connections to bridge engine-internal subsystems and never
depended on the legacy built-in worker.

## Parity vs builtin

| Behavior | Builtin | This worker |
|---|---|---|
| `bridge.invoke` / `bridge.invoke_async` | exact paths, codes `deserialization_error`/`bridge_error` | same |
| Default timeout | 30s (`timeout_ms` overrides) | same |
| `invoke_async` result | `NoResult` (absent) | `null` (SDK functions must return a value) |
| Forward functions | one local function per entry, description `Forward to remote function {id}` | same |
| Expose functions | registered on the remote engine, name defaults to local, real error body forwarded | same |
| Local/control URL | engine-managed | `--url` → `III_URL` → `ws://127.0.0.1:49134` |
| Remote target URL | `config.url` → `III_URL` env → `ws://0.0.0.0:49134` | same legacy-compatible chain; set `config.url` explicitly under Compose |
| Trigger types | none | none |
| Config source | engine `config.yaml` (restart to change) | `configuration` worker entry `bridge`, hot-reload |
| Removing forward/expose entries | restart re-registers cleanly | entry disabled (handler errors); full removal needs worker restart |
