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:
.supported-ridsexists at the nupkg root.- 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_ridsstep inbuild_aot.py) - Packaging Modes — the nupkg structure (where
.supported-ridslives)