---
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
