packages/native/bun-runtime/BRIDGE_CONTRACT.md
This is the contract between the iOS Capacitor host and the full Bun engine framework produced from an iOS-capable Bun fork.
The framework is not a helper executable. iOS local mode must run the backend in process from a signed framework inside the app bundle. The WebView talks to the backend through Capacitor/native IPC, not by opening a TCP connection to a backend port.
Expected bundle:
ElizaBunEngine.xcframework
Expected binary inside each slice:
ElizaBunEngine.framework/ElizaBunEngine
Full-engine production builds link ElizaBunEngine.framework directly through
the CocoaPods dependency and Swift module import. Compatibility/debug builds may
use an optional loader path, but App Store builds must not import dlopen or
dlsym from either the runtime plugin or the engine binary.
The engine binary itself must not import arbitrary dynamic loader, process-spawn, JIT, or executable-memory permission APIs. App Store-compatible builds must declare:
ElizaBunEngineNoJIT = true
ElizaBunEngineExecutionProfile = ios-app-store-nojit
All strings are UTF-8. JSON inputs are UTF-8 JSON strings. Functions return zero on success unless otherwise stated.
const char *eliza_bun_engine_abi_version(void);
const char *eliza_bun_engine_last_error(void);
typedef char *(*eliza_bun_engine_host_call_callback)(
const char *method,
const char *payload_json,
int32_t timeout_ms
);
int32_t eliza_bun_engine_set_host_callback(
eliza_bun_engine_host_call_callback callback
);
int32_t eliza_bun_engine_start(
const char *bundle_path,
const char *argv_json,
const char *env_json,
const char *app_support_dir
);
int32_t eliza_bun_engine_stop(void);
int32_t eliza_bun_engine_is_running(void);
char *eliza_bun_engine_call(
const char *method,
const char *payload_json
);
void eliza_bun_engine_free(void *ptr);
eliza_bun_engine_start boots Bun and runs the staged backend bundle,
normally:
public/agent/agent-bundle.js ios-bridge --stdio
eliza_bun_engine_call is the UI/backend IPC entrypoint. Calls return JSON
objects with this envelope:
{ "ok": true, "result": {} }
Error payloads must use this shape:
{ "ok": false, "error": "message" }
The shim included in this package implements that envelope over newline delimited JSON on stdio:
{ "id": 1, "method": "http_request", "payload": {} }
{ "id": 1, "ok": true, "result": {} }
Bun can call back into native code over the same stdio protocol while a host request is in flight. This is how full-Bun local inference reaches the linked llama.cpp bridge without opening a WebSocket or TCP port:
{ "type": "host_call", "id": "host-1", "method": "llama_generate", "payload": {}, "timeoutMs": 120000 }
{ "type": "host_result", "id": "host-1", "envelope": { "ok": true, "result": {} } }
Required native host-call methods today:
llama_hardware_infollama_load_modelllama_generatellama_freellama_cancelRequired methods today:
status -> { "ready": true, "engine": "bun", "transport": "bun-host-ipc", "bridgeVersion": "bun-ios:3" }http_request / http_fetch with { method, path, headers, body, timeoutMs } -> { status, statusText, headers, body }send_message with { message, conversationId? } -> { reply, text, conversationId, response }path must be a local path beginning with /; absolute URLs are rejected at
the Swift, C, and JS bridge layers.
The full engine must support:
http_request IPC. iOS full-Bun mode must not rely on Bun.serve,
WebSockets, or a WebView-visible TCP listener.fetch, Request, Response, Headers, streams, and buffered bodies.Bun.file, node:fs, node:path, node:crypto, node:buffer, and
package/module resolution needed by packages/agent/dist-mobile-ios.agent-bundle.js.ios-bridge --stdio.The full engine must not require Bun.ffi, dlopen, dlsym, posix_spawn,
fork, execve, system, MAP_JIT, pthread_jit_write_protect_np,
mach_vm_protect, or vm_protect in App Store slices. Local model and runtime
assets may be opened as data files, but they must not be executable payloads or
downloaded native code.
The current ios-bridge dispatches high-traffic foreground routes in process,
buffers legacy local-inference HTTP handlers when needed, and serves native
llama status/generation through Bun host-call IPC. It must not start an
internal HTTP server for iOS full-Bun local mode.
The port is complete only when all of these pass:
bun run --cwd packages/native/bun-runtime build:sim produces an
ElizaBunEngine.xcframework with an iOS Simulator slice.ELIZA_IOS_FULL_BUN_ENGINE=1 bun run --cwd packages/app build:ios:local:sim
builds, installs, and launches in Simulator.bun run --cwd packages/native/bun-runtime smoke:sim boots
public/agent/agent-bundle.js ios-bridge --stdio through the full engine
ABI and invokes status, http_request, and send_message.build:device on a developer-signed sideload.