skills/numericalos-build-iso-cf/SKILL.md
back to source
---
name: numericalos-build-iso-cf
description: >
Produce NumericalOS Linux ISO/initramfs artifacts via Cloudflare (Containers
Sandbox + R2), not WSL and not a local Linux host. Use when the user asks to
build NumericalOS ISO on Cloudflare, avoid WSL, generate boot media without
installing Linux locally, or when numericalos-target-classifier routes here
because the machine has no local cpio/xorriso/WSL but has Cloudflare access.
Triggers: "Cloudflare ISO", "build ISO without WSL", "CF Sandbox initramfs",
"R2 ISO artifacts", "/numericalos-build-iso-cf". Honesty: Workers alone cannot
mkisofs; a Linux container under CF does the pack. Artifact built ≠ boot verified.
---
# Build NumericalOS ISO artifacts on Cloudflare (no WSL)
This skill replaces the **local Linux/WSL** requirement for producing
initramfs + hybrid ISO artifacts. The host can stay Windows (or any OS without
`cpio`/`xorriso`). The **Linux userspace that packs the image** runs inside a
Cloudflare Container (Sandbox SDK). Finished bytes land in **R2** and are
downloaded for local `numericalos-verify-boot` under QEMU.
## Honesty first (Tarski contract)
| Claim you may make | Evidence required |
|---|---|
| "ISO/initramfs object exists in R2" | `wrangler r2 object get` or HEAD with size + sha256 |
| "Structural ISO is bootable (sector present)" | `file` inside the sandbox reported ISO 9660 bootable |
| "NumericalOS boots" | **Only** after `numericalos-verify-boot` on the downloaded artifact |
**Never:**
- Claim a Worker "built an ISO" by itself. V8 isolates do not run `xorriso`.
- Install or enable WSL without explicit user approval (absolute for this repo).
- Call the result "boot verified" because the build job exited 0.
- Skip the classifier when the user asked generically to "build NumericalOS".
Scaffold for the Worker/container lives at:
```
deploy/cloudflare-iso-builder/
```
If that tree is missing, recreate it from this skill before claiming a deploy.
## When the classifier routes here
`numericalos-target-classifier` should pick this skill when:
1. User wants bootable media (ISO / USB / initramfs+kernel), **and**
2. Local machine cannot run `numericalos-build-iso` / `build-initramfs`
(no Linux + cpio/xorriso, no real WSL, no local Docker/Podman for the
*NumericalOS* image itself), **and**
3. Cloudflare is available: `wrangler whoami` succeeds **or** a deployed
builder URL + auth token exist, **and**
4. User prefers CF over installing WSL/Docker for the OS build.
If the machine already has Linux tools, prefer local `numericalos-build-iso`
(faster feedback, no egress). CF is the **Windows / no-WSL bridge**, not the
default for every host.
## Architecture (what actually runs where)
```
Windows agent (this skill)
| 1. export numos.state (local Python OK)
| 2. stage boot/* + state as a source tarball
| 3. POST /build OR wrangler + sandbox scripts
v
CF Worker ──getSandbox()──> Linux Container (Dockerfile)
| - fetch busybox (arch-matched)
| - pack cpio.gz (build-initramfs steps)
| - grub-mkrescue / xorriso (build-iso steps)
| - sha256 + file(1)
v
R2 bucket: numericalos-artifacts/
keys: builds/<id>/numericalos-initramfs-$ARCH.cpio.gz
builds/<id>/numericalos-$ARCH.iso
builds/<id>/manifest.json
v
Download to host → numericalos-verify-boot (local QEMU)
```
**Residuals (name them; do not paper over):**
1. Sandbox image deploy (`wrangler deploy` for Containers) needs **Docker running
once** on *some* machine to push the container image to CF's registry. That is
not WSL and not "build NumericalOS locally" — it is a one-time builder-image
publish. After deploy, ISO builds do not need local Docker.
2. The scaffold Worker currently **base64-ferries** artifact bytes
sandbox→Worker→R2. Fine for ~1 MB initramfs; painful or quota-hostile for a
full kernel ISO (~50–100+ MB). Prefer evolving to **presigned R2 PUT from the
container** when ISOs grow. Until then, prefer `want_iso=false` first
(initramfs only), then pair with a local/kernel path, or use CI path B for
large ISOs.
3. Default kernel fetch inside the container is **Debian `linux-image` via apt**
(x86_64). Network/apt failures → initramfs-only with an explicit warning.
If the account already has the `numericalos-iso-builder` Worker deployed, skip
the Docker step entirely.
## Prerequisites (probe, do not assume)
```sh
# Host (Windows PowerShell or bash)
command -v wrangler || npx wrangler --version
npx wrangler whoami
command -v python py python3
command -v curl wget git
command -v qemu-system-x86_64 # for verify-boot later; not required to build
# Do NOT install WSL. Only report:
wsl.exe --status 2>&1 | head -5
```
Record:
- `CF_ACCOUNT` / whether whoami works
- Whether `deploy/cloudflare-iso-builder` exists in the checkout
- Whether `https://numericalos-iso-builder.<subdomain>.workers.dev` (or custom
host) is already deployed — HEAD `/health`
- Target `ARCH` (default `x86_64` unless user names another)
## Step 0 — handoffs from classifier
You need:
- `ARCH` — canonical arch from `numos_canonical_arch` / `ArchTarget`
- `REPO` — checkout path
- `STATE` — path to verified `numos.state`, or export now (local Python is fine)
- Probe results showing local ISO tools absent / WSL not installed
Export state on the host if needed:
```sh
cd "$REPO"
py -m numos.export_state --out dist
# or: python3 -m numos.export_state --out dist
py -c "from numos.state import verify; verify(open('dist/numos.state').read()); print('state verifies')"
```
## Step 1 — ensure the CF builder exists
### 1a. First-time deploy (needs Docker on *a* machine once)
```sh
cd "$REPO/deploy/cloudflare-iso-builder"
npm install
# Create R2 bucket if missing (name must match wrangler binding):
npx wrangler r2 bucket create numericalos-artifacts
# Build+push container image + deploy Worker (Docker daemon required):
npx wrangler deploy
```
Confirm:
```sh
npx wrangler deployments list
curl -sS "https://numericalos-iso-builder.<workers-subdomain>.workers.dev/health"
# expect: {"ok":true,"service":"numericalos-iso-builder"}
```
### 1b. Builder already deployed
Skip Docker. Use the Worker URL from account inventory or `wrangler deployments list`.
### 1c. No Docker anywhere and no deployed builder
**Stop and say so.** Options to offer the user (do not silently pick):
1. Install Docker Desktop **only** to publish the builder image once (not WSL).
2. Deploy the builder from another Linux/macOS machine with Docker.
3. Use a GitHub Actions workflow that runs the same `scripts/build-iso.sh` on
`ubuntu-latest` and `wrangler r2 object put` the results (still "remote
Linux", not WSL) — see **Fallback path B** below.
Never invent a pure-Workers `mkisofs` implementation.
## Step 2 — stage sources
The sandbox must see the same tree local `build-initramfs` would:
```
boot/bootstrap.sh
boot/numinit.sh
boot/numctl
boot/lib/arch_table.sh
dist/numos.state # verified
COPYING.busybox or GPLv2 notice if redistributing
```
From the repo root, create a source archive the Worker can unpack:
```sh
# Git Bash / MSYS / Linux:
tar -czf /tmp/numericalos-src.tgz \
boot/bootstrap.sh boot/numinit.sh boot/numctl boot/lib/arch_table.sh \
dist/numos.state
# PowerShell alternative: use tar.exe (Windows 10+) the same way.
```
Do **not** ship host kernels from Windows for the default path; the container
fetches a known kernel package or uses a pinned URL from the request body
(see Worker API). Prefer the builder's documented default kernel source so
reproducibility is possible.
## Step 3 — run the remote build
### Preferred: HTTP API on the deployed Worker
```sh
# AUTH_TOKEN must match the Worker secret BUILD_TOKEN (set via wrangler secret)
curl -sS -X POST "$BUILDER_URL/build" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"arch\": \"$ARCH\",
\"want_iso\": true,
\"want_initramfs\": true,
\"source_url\": null
}" \
--data-binary @- <<EOF
# If the Worker accepts multipart: send source tarball as file field "source"
EOF
```
Concrete multipart form (matches scaffold):
```sh
curl -sS -X POST "$BUILDER_URL/build" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-F "arch=$ARCH" \
-F "want_iso=true" \
-F "source=@/tmp/numericalos-src.tgz;type=application/gzip"
```
Poll or wait for JSON:
```json
{
"ok": true,
"build_id": "…",
"arch": "x86_64",
"artifacts": {
"initramfs": "builds/<id>/numericalos-initramfs-x86_64.cpio.gz",
"iso": "builds/<id>/numericalos-x86_64.iso",
"manifest": "builds/<id>/manifest.json"
},
"sha256": { "initramfs": "…", "iso": "…" },
"file_iso": "ISO 9660 … (bootable)",
"boot_verified": false
}
```
If `ok` is false, quote `error` and sandbox logs. Do not retry silently with
different flags; report, then ask.
### Alternate: agent drives Sandbox from local wrangler/dev
Only if you are iterating on the builder itself with Docker local. Not the
normal no-WSL path for end users.
## Step 4 — what the Linux container must execute
The container script (`scripts/build-iso.sh` in the scaffold) implements the
**same packing rules** as `numericalos-build-initramfs` + `numericalos-build-iso`:
1. Install tools if missing: `cpio gzip find xorriso grub-common grub-pc-bin`
(image should already contain them — prefer Dockerfile bake-in).
2. Fetch arch-matched **static busybox** (busybox.net 1.35.0 x86_64/i686 only;
other arches: fail honestly unless user supplied a URL + sha256).
3. Lay out rootfs; place `arch_table.sh` at `/lib/arch_table.sh` **and** keep
`/boot/lib/` if desired; `/init` = `bootstrap.sh` mode 0755.
4. `cpio --format=newc --owner=root:root | gzip -9`.
5. Stage ISO tree + GRUB serial + graphical menu entries.
6. `grub-mkrescue -o numericalos-$ARCH.iso …` (or documented xorriso fallback).
7. Write `manifest.json` with sha256, sizes, busybox URL, kernel source, git
commit if known, `boot_verified: false`.
**Kernel:** default scaffold pulls a **pinned** Debian/Ubuntu `linux-image`
`.deb` and extracts `vmlinuz`, or accepts `kernel_url` + `kernel_sha256` in the
build request. Never claim "self-contained OS kernel from NumericalOS" — the
project does not ship a kernel.
**GPLv2:** busybox redistribution requires licence text in the artifact or
manifest note. Include `COPYING` / state redistribution case in the report.
## Step 5 — download artifacts to the host
```sh
# --remote is required; without it wrangler looks at a local miniflare store.
npx wrangler r2 object get numericalos-artifacts/$KEY_ISO \
--remote --file "./numericalos-$ARCH.iso"
npx wrangler r2 object get numericalos-artifacts/$KEY_INITRD \
--remote --file "./numericalos-initramfs-$ARCH.cpio.gz"
npx wrangler r2 object get numericalos-artifacts/$KEY_MANIFEST \
--remote --file "./manifest.json"
```
Verify sizes and sha256 match the manifest **on the host** before any boot claim.
## Step 6 — hand off to verify-boot (local QEMU)
Cloudflare Sandbox is **not** a substitute for QEMU boot observation in this
skill family. Download the ISO (or kernel+initrd), then run
`numericalos-verify-boot` with the host's QEMU (e.g. MSYS2
`mingw-w64-x86_64-qemu` on Windows).
Only after that skill's PASS may anyone say NumericalOS booted — and only for
that artifact hash, arch, and emulator version.
## Fallback path B — CI Linux → R2 (no Sandbox)
When Containers cannot be deployed:
```yaml
# .github/workflows/numericalos-iso.yml (sketch)
# runs-on: ubuntu-latest
# steps: checkout → run scripts/build-iso.sh → wrangler r2 object put
```
Still "remote Linux via Cloudflare-adjacent artifact store", still not WSL.
Report path B explicitly when used. Prefer the Sandbox Worker once available so
the agent does not depend on GitHub for every build.
## Failure modes
| Symptom | Cause / fix |
|---|---|
| `wrangler whoami` fails | No CF auth; stop |
| `/health` 404 | Builder not deployed; Step 1 |
| Docker required error on deploy | Need one-time image publish machine |
| Sandbox missing `xorriso` | Rebuild Dockerfile with packages; redeploy |
| `No working init found` later in QEMU | initramfs layout / mode — same as local skill |
| `arch_table.sh: not found` | `NUMOS_LIB` path — put table at `/lib/` |
| ISO not `(bootable)` | grub-mkrescue failed; check logs |
| Worker timeout | Build too long; raise limits or split initramfs-only first |
| Free-tier / quota errors | Surface loudly; do not swallow |
## Reporting template
```
Mode: Cloudflare ISO builder (no WSL)
Probed host: <OS> — local xorriso/cpio/WSL: absent
Builder: <Worker URL or "CI path B">
Build id: <id>
Arch: <ARCH>
Artifacts:
initramfs: r2://numericalos-artifacts/... sha256=… size=…
iso: r2://numericalos-artifacts/... sha256=… size=…
file(1): <exact line>
Kernel source: <url or package> (NumericalOS does not ship a kernel)
Busybox: <url> verified=<yes/no>
State: <path> verify=<ok>
boot_verified: false
Next: numericalos-verify-boot on downloaded ISO under local QEMU
```
## Related skills
- `numericalos-target-classifier` — run first; may route here on Windows
- `numericalos-build-initramfs` / `numericalos-build-iso` — local Linux equivalents
- `numericalos-verify-boot` — only skill that may claim a boot
- Cloudflare plugin skills: `sandbox-sdk`, `wrangler` — for deploying the builder