# Developer Guide This guide describes the current openUF implementation. The daemon is a single-process, single-threaded OpenWrt service. Its main loop wakes once per second and schedules discovery, LLDP, and controller inform work. ## Safety first openUF runs as root. It can replace managed Wi-Fi configuration, create VLAN devices, send raw Ethernet frames, reboot the device, and persist adoption credentials. Do not run the daemon itself on a development workstation. Compile it with the OpenWrt toolchain and perform runtime tests on a disposable OpenWrt access point. Protocol debug level 2 can log decrypted credentials. Never enable it by default, attach those logs to issues, or commit controller payload captures. ## Source layout | Module | Responsibility | | --- | --- | | `src/announce.c` | UniFi layer-2 UDP discovery. | | `src/clients.c` | Wireless and bridge client telemetry. | | `src/config.c` | Static daemon configuration parser and defaults. | | `src/crypto.c` | AES-CBC, AES-GCM, and encoding helpers. | | `src/http.c` | Minimal HTTP/1.0 transport. | | `src/inform/inform.c` | One inform request/response exchange. | | `src/inform/packet.c` | TNBU binary envelope codec. | | `src/inform/payload.c` | Inform JSON telemetry assembly. | | `src/inform/response.c` | Controller command and provisioning dispatch. | | `src/lldp.c` | LLDP frame transmission and neighbor collection. | | `src/main.c` | Daemon lifecycle and one-second scheduler. | | `src/models.c` | Emulated hardware model registry. | | `src/state.c` | Persistent adoption and controller state. | | `src/sysinfo.c` | Kernel and nl80211 device telemetry. | | `src/wlan/common.c` | Shared Wi-Fi translations and validators. | | `src/wlan/legacy.c` | Legacy system_cfg translation. | | `src/wlan/provision.c` | Modern setstate Wi-Fi provisioning. | | `src/wlan/radio.c` | Runtime radio mapping and radio settings. | | `src/wlan/telemetry.c` | Managed VAP telemetry from UCI and nl80211. | | `src/wlan/uci.c` | Reusable UCI, VLAN, steering, and cleanup operations. | Public contracts remain in `src/*.h`. The `src/inform/` and `src/wlan/` directories contain private implementation units; their `*_internal.h` files are not stable interfaces for other subsystems. ## Runtime architecture `main()` loads static configuration, persistent controller state, and the emulated model. It discovers the LAN MAC/IP and an initial controller from the default route when no explicit controller is configured. The main loop then schedules: 1. `announce_send()` for UniFi UDP discovery. 2. `lldp_send_frame()` for each model Ethernet port. 3. `inform_send()` for adoption, telemetry, commands, and provisioning. See [[Runtime Flow|Runtime-Flow]] for the decision flow and [[Function Call Graph|Call-Graph]] for generated caller/callee relationships. ## Inform and adoption invariants - The TNBU layout, big-endian fields, flag meanings, authenticated header, and cipher selection are controller compatibility constraints. - An unadopted device always encrypts with `DEFAULT_AUTH_KEY`, even if stale state contains another key. - The controller response is decoded before command dispatch. Adoption can be completed through either legacy `set-adopt` or modern `setparam` data. - `cfgversion` records configuration that was successfully applied locally. Failed provisioning resets it to `"0"` so the controller retries. - Increase `OPENUF_CONFIG_SCHEMA` only when an existing persisted configuration must be reapplied after an upgrade. ## Wi-Fi ownership and provisioning Only UCI `wifi-iface` sections prefixed with `openuf_` belong to this daemon. Cleanup must preserve every unrelated section. Controller VAP ObjectIds are stored in `openuf_vap_id` so client topology remains stable across reprovision. Modern `setstate` data is applied by `wlan_apply_config()`. Legacy newline-separated `system_cfg` data is first translated to the same JSON shape by `wlan_apply_system_cfg()`, keeping one provisioning path responsible for UCI commits and radio startup. Model band and port assumptions belong in `models.c`. The runtime radio mapper may resolve a model band to a different local PHY, but protocol and telemetry code must not invent model-specific mappings. ## Common development tasks ### Add telemetry 1. Add a bounded reader to `sysinfo.c`, `clients.c`, or another focused module. 2. Add the controller field in `inform/payload.c`. 3. Document units, fallback behavior, ownership, and any sensitive content. 4. Regenerate this Wiki and cross-build the package. ### Add a controller command 1. Extend dispatch in `inform/response.c`. 2. Validate controller values before changing state or invoking a command. 3. Save state only after a coherent transition. 4. Preserve adoption-key and `cfgversion` behavior. ### Add a Wi-Fi setting 1. Parse controller aliases in `wlan/provision.c` or `wlan/legacy.c`. 2. Put reusable UCI work in `wlan/uci.c` and translations in `wlan/common.c`. 3. Report the effective value from `wlan/telemetry.c` when the controller expects it in `vap_table`. 4. Test on device; a successful cross-build cannot validate netifd/hostapd behavior. ### Add or change a model Update `ufmodel.h`, the complete model entry in `models.c`, and every affected telemetry or WLAN consumer together. Do not scatter model checks across the protocol implementation. ## Build and validation From the OpenWrt root: ```sh make package/OpenUniFi/compile ``` Use `V=s` for detailed compiler diagnostics. If a clean package rebuild is needed, clean only this package: ```sh make package/OpenUniFi/clean make package/OpenUniFi/compile ``` `Makefile.standalone` is only for compiling directly on an OpenWrt device with development packages installed. It is not a host-side test substitute. ## Documentation workflow Generate pages after changing C code or architecture: ```sh ./scripts/docs/generate-wiki.py ``` Run the linter-style drift and structure check in CI or before committing: ```sh ./scripts/docs/generate-wiki.py --check ``` The checker parses C functions recursively, rebuilds internal call edges, validates required runtime entry points, rejects known non-English comment fragments, and limits each implementation unit to 900 lines. ## Publishing to the Gitea Wiki Gitea stores a repository Wiki in a separate Git repository whose URL normally ends in `.wiki.git`. The generated `wiki/` directory is ready for that remote. Run `scripts/docs/publish-wiki.sh` from a trusted machine with suitable credentials. It derives the Wiki remote from `origin`; an explicit URL may be passed when needed. The publisher regenerates and checks the pages, updates only the generated Markdown files, commits changed pages, and pushes them to the Wiki repository.