# 2026-07-13 -- D-113(a2) COMPLETE: the config.xml path is DELETED

Operator ruling ("I'll defer to your lean"): the OPNsense `config.xml` template is **deleted, not
reduced**.

## Why deleted rather than reduced

The (a2) plan was to shrink the template to a minimal bootstrap (sshd + root key + console + a
seeded API key). That turned out to be **unnecessary** -- every one of those is covered without a
`config.xml` at all:

| bootstrap need | how it is met now |
|---|---|
| sshd + root key | the **D-112(c) console bootstrap** (proven on Office1) |
| an API key | **`opnsense-bootstrap-apikey.sh`** -- calls OPNsense's OWN model over SSH; no GUI click, no re-implemented crypto |
| DHCP / firewall / interfaces | **the REST API** (`opnsense-api.sh`), proven read AND write |

So the provisioning chain contains **no `config.xml` anywhere**:

    boot factory nano -> console bootstrap -> mint API key -> configure over REST

A `config.xml` renderer that nobody should ever run is **not a safety net -- it is a loaded gun
pointed at a live router.** The 2026-07-13 safety sweep is the evidence: the repo still contained
runbook steps telling an operator to render a config and push it to the edge, which by then would
have **clobbered live API-managed DHCP**.

## Deleted

- `opentofu/templates/opnsense-config.xml.tmpl`
- `scripts/opnsense-render-config.sh` + `tests/opnsense-render-config/`
- `scripts/opnsense-build-config-iso.sh` + `tests/opnsense-build-config-iso/`
- the `opnsense-edge` module's `config_seed` volume + cdrom disk, and its `config_iso_path` var
- the `xorriso`/`genisoimage` prereq (it existed ONLY for the ISO builder)

All remain in git history. `opentofu/templates/README.md` is now a **tombstone** explaining what
happened and what to use instead -- people will go looking for that template.

## The live change, and why it was safe

Removing the module's ISO wiring touches an INSTANTIATED resource
(`libvirt_volume.config_seed` is in tfstate). Measured with `tofu plan` BEFORE applying:

    module.office1_opnsense.libvirt_domain.vm will be updated IN-PLACE
    module.office1_opnsense.libvirt_volume.config_seed will be destroyed
    Plan: 0 to add, 1 to change, 1 to destroy.

**No replacement** of the running domain -- confirmed explicitly (a replacement would have
destroyed the live edge). Only the cdrom entry is removed from the disk list and the inert ISO
volume is dropped.

### CORRECTION -- "updated in-place" DOES NOT MEAN "no restart" (measured, 2026-07-13)

**The apply RESTARTED the guest.** This was predicted NOT to happen, and that prediction was
WRONG. Measured: uptime went from `8:36` to `6 secs` across the apply. The dmacvicar/libvirt
provider redefines the domain to apply a disk-list change and **bounces it** -- there was a brief
outage (~30s) during which the edge was not routing and not serving DHCP.

**`libvirt_domain ... will be updated in-place` means the RESOURCE is not replaced. It says
NOTHING about whether the GUEST keeps running.** Do not read it as "no interruption".

**This matters for Stage 3.** Any future `tofu apply` that touches a `libvirt_domain`'s devices --
on a DC edge, or on a node VM -- should be assumed to BOUNCE THE GUEST, and must be scheduled and
gated as an outage, not as a no-op config tidy-up.

Recovery was clean and needed no intervention: post-boot, `kea-dhcp4` is running and bound to
`10.10.0.1:67` with the subnet/pool intact, WAN/LAN/default-route up, egress 0.0% loss, 8 LAN pass
rules in pf, serial console + getty alive. `tofu plan` reports "No changes" -- repo and state are
back in sync.

**Applying it now was the SAFE choice, not the risky one.** Leaving the repo/state divergence in
place would mean the next `tofu apply` -- e.g. while building DC1's substrate in Stage 3 --
sweeps up this pending change as a **surprise side effect on the running Office1 edge, mid-stage**.
Deliberate and gated beats incidental and unnoticed.

## A lint guard did its job

`repo-lint`'s **L3** rule (runbooks must not reference missing scripts) went RED on the tombstone
notes, because they named the deleted paths. The rule was RIGHT, and it has no opt-out (only L4
does). Rather than weaken a guard that exists to stop runbooks pointing at dead scripts, the
tombstones were reworded to name the bare filename instead of the `scripts/` path. The guard stays
fully intact; the information is preserved.

## Verification

`repo-lint` 0 fail. `opentofu-validate` PASS -- root + **10/10 modules** standalone. Gauntlet green.

## Revert

    git revert <this commit>          # restores the template, renderer, ISO builder, harnesses
    cd opentofu && tofu apply         # re-creates the (inert) config_seed volume + cdrom

Nothing depends on the ISO: it was never read by anything (D-112).
