Newer
Older
openstack-caracal-dc-dc / docs / changelog-20260713-opnsense-api-write-proven.md

2026-07-13 -- D-113(a2) write path PROVEN on the live edge

The READ half was proven earlier today. This closes the other half: configuration written through the REST API reaches the running daemon. (a2) is now demonstrated end to end, not argued.

What was executed (live, operator-approved)

POST kea/dhcpv4/set_subnet/93473635-f2aa-5c0a-986c-438e59088c6c   -> {"result":"saved"}
POST kea/service/reconfigure                                       -> {"status":"ok"}
GET  kea/service/status                                            -> {"status":"running"}

Deliberately IDEMPOTENT: the payload was the exact values read back from the API moments before (subnet 10.10.0.0/24, pools 10.10.0.100-10.10.0.199, routers/domain_name_servers 10.10.0.1, description "LAN DHCP"). Nothing about the edge's behaviour should change -- the point was to exercise the WRITE path, not to alter the edge.

GROUND TRUTH after the write (not the API's self-report)

The API saying {"result":"saved"} is the service's own verdict. What was actually measured on the box:

kea-dhcp4 pid 30027 (RESTARTED), bound udp4 10.10.0.1:67

Kea's OWN generated config (/usr/local/etc/kea/kea-dhcp4.conf -- the daemon's JSON, not OPNsense's XML):

interfaces           : ['vtnet0']
subnet               : 10.10.0.0/24
pools                : ['10.10.0.100-10.10.0.199']
routers              : 10.10.0.1
domain-name-servers  : 10.10.0.1
domain-name          : office1.vr1.cloud.neumatrix.local

Router unaffected (the endpoint touches only Kea, but verify, never assume): WAN 172.30.1.2, LAN 10.10.0.1, default route 172.30.1.1, 8 LAN pass rules loaded in pf, egress to 1.1.1.1 at 0.0% packet loss.

The full chain works: API write -> OPNsense renders -> Kea loads -> daemon serves.

Payload shape (measured -- do not infer this)

OPNsense GET returns select fields as {value: {selected: 0|1}}; SET expects the comma-joined selected values. A GET response is therefore NOT directly re-POSTable -- it must be flattened:

{"routers": {"10.10.0.1": {"value": "10.10.0.1", "selected": 1}}}   # what GET returns
{"routers": "10.10.0.1"}                                            # what SET expects

kea/service/reconfigure is the apply verb (ServiceController extends ApiMutableServiceControllerBase; $internalServiceName = 'kea'). Without it the config is saved but the daemon is not reloaded.

Consequence: the config.xml push path is now RETIRED IN PRACTICE

DHCP on this edge is now API-managed. Pushing a full rendered config.xml would clobber it. Do not run the old scp/install/reboot path against Office1. The template must be reduced to a minimal bootstrap (sshd + root key + console) before it is used again -- that is the next step under D-113(a2), and it is NOT yet done.

Revert

Re-POST the previous values (they are unchanged, so nothing to undo), or from the GUI: Services > Kea DHCP > DHCPv4 > Subnets. The pre-API config.xml backups remain in /conf/backup/ on the edge.