Hi Cedrik,
Thanks a lot for the detailed answer and the pointers to the source. After going through both files, all four points are now clear:
1. state — sourced directly from Kea's native lease state (lease.get("state", 0) in get_kea_leases.py), so always an integer (Kea's enum: 0=default, 1=declined, 2=expired-reclaimed, 3=released). Stable as long as Kea's own data model is.
2. is_reserved — your explanation matches the code in get_reservation_keys: any of "hwaddr", "duid", "client_id", with the array carrying one entry per matched identifier (and the subnet-id scoping comment on build_reserved_matches is a nice touch — explains exactly why client roaming doesn't false-positive).
3. Top-level interfaces — looking at LeasesController.php confirms it's the json_encode behaviour on an empty PHP array: $interfaces starts empty and only becomes associative once a record's interface resolves via the if-map. With zero matching records, it stays empty and serializes as a JSON array rather than an object. So consumers need to handle both shapes defensively (or, in our case, simply ignore the field — the per-interface map can be rebuilt from each row's if / if_descr).
4. API stability — "major redesigns done, mostly bug fixes from here" is exactly the commitment we needed. The careful subnet-id reasoning in the script reinforces that the design is being maintained thoughtfully. Good enough basis to commit to the current types on the exporter side.
I'll link this thread back at AthennaMind/opnsense-exporter#105 for the record. Thanks again for taking the time.
— Golden Garlic (garlicKim21 on GitHub)
Thanks a lot for the detailed answer and the pointers to the source. After going through both files, all four points are now clear:
1. state — sourced directly from Kea's native lease state (lease.get("state", 0) in get_kea_leases.py), so always an integer (Kea's enum: 0=default, 1=declined, 2=expired-reclaimed, 3=released). Stable as long as Kea's own data model is.
2. is_reserved — your explanation matches the code in get_reservation_keys: any of "hwaddr", "duid", "client_id", with the array carrying one entry per matched identifier (and the subnet-id scoping comment on build_reserved_matches is a nice touch — explains exactly why client roaming doesn't false-positive).
3. Top-level interfaces — looking at LeasesController.php confirms it's the json_encode behaviour on an empty PHP array: $interfaces starts empty and only becomes associative once a record's interface resolves via the if-map. With zero matching records, it stays empty and serializes as a JSON array rather than an object. So consumers need to handle both shapes defensively (or, in our case, simply ignore the field — the per-interface map can be rebuilt from each row's if / if_descr).
4. API stability — "major redesigns done, mostly bug fixes from here" is exactly the commitment we needed. The careful subnet-id reasoning in the script reinforces that the design is being maintained thoughtfully. Good enough basis to commit to the current types on the exporter side.
I'll link this thread back at AthennaMind/opnsense-exporter#105 for the record. Thanks again for taking the time.
— Golden Garlic (garlicKim21 on GitHub)
"