Runtime Architecture Check
Every nupkg produced by build_aot.py (or by build_both.py) carries a .supported-rids sidecar manifest at the nupkg root. A deploy consumer can 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. No current script in this repo implements this check (the worked example below is a pattern to adopt, not something already wired into scripts/create_worker.py) — add it to your own deploy tooling if you need it.
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.
A worked example
The check is straightforward: fetch the nupkg, read the sidecar, compare against the local RID, fail fast if unsupported — 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) |
Not every AOT nupkg necessarily carries the sidecar (e.g. one built before write_supported_rids existed, or by a build script that predates it) — the check above skips itself when the sidecar is missing, and the consumer falls back to letting the WM's NativeDllEngine fail at load time (preserves back-compat rather than blocking a deploy on a false negative).
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
No worker in this repo currently ships a dedicated AOT smoke test, but the pattern (worth adopting for a worker that needs one) is a local script that 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)