# bootapi HTTP endpoints ## HTTP and HTTPS — the boot path is plain HTTP by design bootapi always serves the boot path (`/ipxe`, `/boot/ipxe`, `/ks`) over **plain HTTP** on `BOOTAPI_LISTEN_ADDR`. A PXE installer environment has no internal-CA trust, so an HTTPS-only boot URL (with our private CA cert) would fail the TLS handshake. iPXE and the kickstart therefore use `http://` URLs (from `BOOTAPI_BASE_URL`). Optionally bootapi *also* serves HTTPS in parallel (`BOOTAPI_TLS_LISTEN_ADDR` + cert/key), for clients that do trust the CA. The Kubernetes exposure must **not** blanket-301 HTTP→HTTPS for the boot endpoints — see [deployment.md](deployment.md#gateway-http-and-https). ## The PXE flow ``` DHCP ── next-server + filename (ipxe.efi / undionly.kpxe) ──▶ firmware loads iPXE iPXE ── GET http:///ipxe/ ─────────────────────▶ bootapi renders a boot script boot ── kernel + initrd + inst.ks=http:///ks/ ─▶ Anaconda fetches the kickstart KS ── GET http:///ks/ ──────────────────────▶ bootapi renders the kickstart post ── POST http:///provisioned/ (token) ────▶ bootapi clears pxe_enabled in NetBox ``` This mirrors Cobbler, which chained iPXE to `/cblr/svc/op/gpxe/mac/`, served a per-system script carrying `inst.ks=`, and cleared `netboot_enabled` at the end of the install. ## Endpoints | Method | Path | Purpose | |--------|------|---------| | GET | `/ipxe/{mac}` | iPXE boot script for the host owning `{mac}`. `{mac}` may use `:`/`-`/`.` separators or be bare hex; a trailing `.ipxe` is stripped. | | GET | `/boot/ipxe?mac=...` | Query-string alias of `/ipxe/{mac}`. | | GET | `/ks/{ident}` | Rendered kickstart. `{ident}` is a MAC (auto-detected) or a hostname; trailing `.ks`/`.cfg` is stripped. | | POST | `/provisioned/{ident}` | End-of-kickstart callback; clears `pxe_enabled` in NetBox. **Token-guarded** (`Authorization: Bearer `). | | GET | `/healthz` | Liveness: always `200 ok`. | | GET | `/readyz` | Readiness: `200` once templates parsed. Does **not** probe NetBox. | | GET | `/metrics` | Prometheus metrics (see below). | ## Host identification A booting host is identified by the **MAC** of the NIC it PXE-booted from (`/ipxe/{mac}`), resolved via NetBox `GET /api/dcim/interfaces/?mac_address=` → device → primary IP, platform, role, interfaces. `/ks/{ident}` and `/provisioned/{ident}` also accept a **hostname** (NetBox device name). ## Per-host PXE-enable gate (`pxe_enabled`) `/ipxe/{mac}` checks the device's `pxe_enabled` NetBox custom field (Cobbler's `netboot_enabled`): - **unset or `true`** → normal installer boot script. - **`false`** → the safe **local-boot** fallback, *even for a known host*, so a machine that has already been provisioned does not re-install on its next PXE. The `/provisioned/{ident}` callback (called from the kickstart `%post`) sets the field to `false` when the install finishes; so a host installs once, then gates itself off. Flip it back to `true` in NetBox to re-image. ## Error behavior (deliberate) The boot endpoints fail **differently** on an unknown host, because the cost of a wrong answer differs: - **`/ipxe/{mac}` never returns 404.** iPXE needs a syntactically valid script. An unknown MAC — or *any* NetBox error, or a gated host — returns HTTP 200 with the **fallback script** selected by `BOOTAPI_UNKNOWN_MAC_FALLBACK`: - `local` (default): `sanboot` the local disk. Safe: an accidental PXE (or a NetBox blip) boots the installed OS; a genuinely new machine loops back to PXE next time. We never start an installer for a machine we can't identify. - `shell`: interactive iPXE shell for an operator to read `${net0/mac}` and register it. Opt-in; unsafe as a default because it halts the boot. - **`/ks/{ident}` returns 404** for an unknown host (502 on a NetBox error). By the time Anaconda fetches the kickstart it has committed to installing; a clear failure beats an empty/wrong kickstart. ## The provisioned callback `POST /provisioned/{ident}` requires the shared token in an `Authorization: Bearer` (or bare `token`) header. Responses: `204` on success, `401` on a bad/missing token, `404` for an unknown host, `503` when no `BOOTAPI_PROVISION_TOKEN` is configured (fail closed), `502` on a NetBox write failure. The default kickstart templates call it from `%post` over plain HTTP (the token authenticates the call; no CA trust needed at install time). ## Metrics All on `/metrics`, prefix `bootapi_`: - `bootapi_http_requests_total{endpoint,status}` — endpoint = `ipxe|ks|healthz|readyz`, status = `2xx|3xx|4xx|5xx`. - `bootapi_render_total{kind,result}` — kind = `kickstart|ipxe`, result = `ok|error`. - `bootapi_netbox_lookups_total{field,result}` — field = `mac|name`, result = `ok|notfound|error`. - `bootapi_netbox_lookup_duration_seconds{field}` — histogram. - `bootapi_netbox_cache_hits_total` / `bootapi_netbox_cache_misses_total`. - `bootapi_provisioned_total{result}` — result = `ok|unauthorized|notfound|error|disabled`. - `bootapi_ipxe_gated_total` — known hosts served local-boot because `pxe_enabled=false`. - `bootapi_template_sync_total` / `bootapi_template_sync_failures_total` / `bootapi_template_generation` — template git-sync (see [template-authoring.md](template-authoring.md)). - standard Go/process collectors.