Skip to content

Runtime Architecture Check

Every nupkg produced by build_aot.py (or by build_both.sh) carries a .supported-rids sidecar manifest at the nupkg root. Consumers (e.g. binance.py) read it before calling CreateWorker to assert the local RID is supported — failing fast with a clear error rather than letting the WM's NativeDllEngine fail at load time with a cryptic "library not found" message.

The sidecar format

A plain text file at the nupkg root, one RID per line, in build order:

linux-arm64
linux-x64

The order matters: it's the same order the build script processed the RIDs. Consumers may rely on the first entry as a "default" RID.

How binance.py consumes it

In examples/binance.py (the canonical consumer), the check happens before any CreateWorker call:

def _current_rid() -> str:
    machine = platform.machine().lower()
    system = platform.system().lower()
    arch = {"x86_64": "x64", "amd64": "x64",
            "aarch64": "arm64", "arm64": "arm64"}.get(machine, machine)
    os_ = {"linux": "linux", "darwin": "osx",
           "windows": "win"}.get(system, system)
    return f"{os_}-{arch}"


async def _assert_runtime_supported(worker_url: str) -> None:
    """Fetch the nupkg header and verify the local RID is in
    .supported-rids. Skip if the sidecar is missing (back-compat with
    pre-0.0.8 nupkgs)."""
    try:
        nupkg_bytes = await run_in_executor(urllib.request.urlopen, worker_url)
    except Exception:
        return  # network error — let the WM fail at load time
    with zipfile.ZipFile(io.BytesIO(nupkg_bytes)) as zf:
        if ".supported-rids" not in zf.namelist():
            return
        supported = zf.read(".supported-rids").decode().splitlines()
    current = _current_rid()
    if current not in supported:
        raise UnsupportedWorkerRuntimeError(
            f"Worker nupkg supports {supported}, but you are on {current}. "
            f"Rebuild with --rids {current} or run on a supported host.")

The UnsupportedWorkerRuntimeError is a clear, actionable error. The user sees "supports [linux-arm64, linux-x64], but you are on osx-arm64" and knows what to do.

What happens on each platform

Local RID Supported nupkg RIDs Result
linux-x64 [linux-x64] ✓ pass
linux-x64 [linux-arm64] ✗ fail: "supports [linux-arm64], but you are on linux-x64"
linux-arm64 [linux-arm64, linux-x64] ✓ pass
osx-arm64 [linux-arm64, linux-x64] ✗ fail: "supports [linux-arm64, linux-x64], but you are on osx-arm64"
win-x64 [linux-arm64, linux-x64] ✗ fail: same message
linux-arm64 nupkg without sidecar ✓ pass (back-compat: trust the legacy flow)

The sidecar was added in nupkg version 0.0.8-sha.<hash> (commit 06fb5a0 in the worker repo). Older nupkgs (e.g. 0.0.8-sha.ed20830e) do not carry it; the consumer skips the check and lets the WM's NativeDllEngine fail at load time (preserves back-compat).

Adding the check to a new consumer

The check is a ~30-line function (above). Any consumer that registers a worker with the WM can use it:

async def create_and_start_worker(api, worker_url, ...):
    await _assert_runtime_supported(worker_url)
    # proceed with CreateWorker ...

Or wrap it at the deployment level: a CI step that runs on the deploy target can read the sidecar and fail the deploy with the same UnsupportedWorkerRuntimeError message before the WM is even involved.

The sidecar's role in CI / pre-flight

The script WebSocketManagerController/scripts/smoke_test_aot.py verifies the sidecar is present and correct as part of the AOT smoke test (it's published as a local script, not in CI). The smoke test asserts:

  1. .supported-rids exists at the nupkg root.
  2. Its contents list exactly the rids that were built, in build order, one per line.

This catches the case where the build script accidentally drops the sidecar (e.g. if a future refactor moves the staging logic and the .supported-rids write is forgotten).

See also

  • Build Script — how the sidecar is written (the write_supported_rids step in build_aot.py)
  • Packaging Modes — the nupkg structure (where .supported-rids lives)