balancer-lite-lua.dlog 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # balancer-lite-lua.dlog — Deployment Log
  2. ## 2026-09-04 Initial Prototype
  3. ### What
  4. Created new workspace project `balancer-lite-lua/` for OpenWrt 22.03 Lua 5.1 port of
  5. balancer-lite's hysteresis watchdog, flap detection, signed webhook outbox, and retention store.
  6. Thin-layer only (~800–1000 LOC) — composes with mwan3/netifd rather than replacing them.
  7. ### Commit
  8. `09fad45` — "Initial Lua 5.1 prototype for OpenWrt 22.03"
  9. ### Files
  10. - `src/balancerlite/state.lua` — 8-state hysteresis machine (INIT, WAN_A/B_PRIMARY, SWITCHING_TO_A/B, DEGRADED, BOTH_DOWN)
  11. - `src/balancerlite/probes.lua` — ICMP/TCP/DNS health checks via ping/nc/nslookup subprocesses
  12. - `src/balancerlite/store.lua` — JSONL append-only event log + compaction
  13. - `src/balancerlite/outbox.lua` — Signed webhook retry queue with exponential backoff + circuit breaker
  14. - `src/balancerlite/sha256.lua` — SHA-256/HMAC-SHA256 via `openssl dgst` CLI
  15. - `src/balancerlite/config.lua` — UCI config file parser
  16. - `src/balancerlite/main.lua` — procd-compatible poll loop daemon
  17. - `src/balancerlite/json.lua` — Pure-Lua JSON encoder (JSON.stringify only)
  18. - `etc/init.d/balancerlite` — procd init script
  19. - `etc/config/balancerlite` — UCI config example
  20. - `Makefile` — lint + test targets
  21. - `tests/state.lua` — 8 state machine tests (all passing)
  22. - `tests/sha256.lua` — SHA-256 + HMAC-SHA256 test vectors (all passing)
  23. - `MEMORY.md`, `README.md`
  24. ### Test Results
  25. ```
  26. lua5.1 tests/state.lua → PASS: state (8/8 tests)
  27. lua5.1 tests/sha256.lua → PASS: sha256 (3/3 vectors)
  28. ```
  29. All modules pass `luac5.1 -p` (parse check).
  30. ### Verification Commands
  31. ```sh
  32. cd /root/.openclaw/workspace/balancer-lite-lua
  33. make test # runs all tests
  34. make lint # runs luac5.1 -p on all modules
  35. ```
  36. ### Remote
  37. `https://git3.techno-world.net/lrosales/balancer-lite-lua.git`
  38. ⚠️ Repo does not exist yet on git3 — creation via Gogs API returned 401.
  39. Manual repo creation needed on https://git3.techno-world.net first.
  40. ### Notes
  41. - HMAC-SHA256 uses `openssl dgst -hmac` CLI (openssl-util package on OpenWrt 22.03)
  42. - No lua-crypto dependency; no raw sockets; all probes via subprocess
  43. - State machine verified against Go source: 8 states, same transition logic
  44. - SWITCHING_TO_* are one-drive-cycle intermediate states
  45. - BOTH_DOWN transitions directly to stable primary (no intermediate)
  46. - BOTH_DOWN clears switch_times (flap detection not counted during all-down)
  47. - DEGRADED entry: flap check counts #record_switch calls; needs `flap_threshold` of them
  48. ### Remaining Work
  49. 1. Create empty repo on git3.techno-world.net, then `git push -u origin master`
  50. 2. Write `src/balancerlite/routing.lua` (policy routing via `ip` commands)
  51. 3. Write `src/balancerlite/wg.lua` (WireGuard endpoint re-point via `wg set`)
  52. 4. Write `balancerlite-ctl` CLI tool (status/events/switch/compact)
  53. 5. Runtime test on `openwrt-testbed`
  54. ## 2026-09-04 Routing/WG/CTL + integration
  55. ### What
  56. Completed the OpenWrt 22.03 Lua 5.1 prototype by adding the three
  57. remaining subsystems (policy routing, WireGuard endpoint re-point,
  58. balancerlite-ctl CLI) and wiring them into the daemon. Plus fixes for
  59. three real bugs found while wiring: (1) `probes.lua`/`store.lua`
  60. didn't expose `new` to their return tables, (2) `json.lua`'s
  61. `gsub('\0', ...)` was a Lua 5.1 zero-width pattern bug — every
  62. character was getting `\u0000` injected; replaced with `string.find`
  63. plain-mode + manual rebuild, (3) `config.lua`'s `s and get_opt(...)
  64. or default` idiom silently coerced `false` values back to `true`
  65. when the option was a bool — replaced with explicit `opt()` helper.
  66. ### Files added/changed
  67. - `src/balancerlite/routing.lua` (199 LOC) — iproute2 actuator:
  68. `apply_default` (boot), `switch_to(wan)` (failover), `verify(wan)`
  69. (self-check). Pure helpers for command building + injectable
  70. `exec(cmd)->rc` for tests without root.
  71. - `src/balancerlite/wg.lua` (185 LOC) — WireGuard actuator:
  72. `set_endpoint(wan)` (`wg set ... endpoint ...` + `wg syncconf`),
  73. `check_handshake(max_age)` for staleness detection, `prewarm()`
  74. for standby path. Same pure/injectable split.
  75. - `src/balancerlite/ctl.lua` (235 LOC) — CLI logic: status, events,
  76. switch, compact, verify. Pure request builders + file-IPC for
  77. status.json/control.json round trip.
  78. - `src/balancerlite/ctl_main.lua` — shell entry point with `--`
  79. separator handling.
  80. - `bin/balancerlite` + `bin/balancerlite-ctl` — POSIX shell
  81. wrappers for `/usr/bin/`.
  82. - `tests/routing.lua` (32 tests), `tests/wg.lua` (35 tests),
  83. `tests/ctl.lua` (42 tests), `tests/smoke.lua` (17 tests).
  84. - `src/balancerlite/json.lua` — fixed `\0` zero-width gsub bug.
  85. - `src/balancerlite/config.lua` — added `opt()` helper, removed
  86. `X and Y or Z` falsy-coercion footgun.
  87. - `src/balancerlite/probes.lua` + `store.lua` — exposed `new` in
  88. return tables.
  89. - `src/balancerlite/main.lua` — wired routing/wg/ctl, status.json
  90. writer, control.json poller; moved `log` helper before subsystems.
  91. - `etc/config/balancerlite.example` — UCI example.
  92. - `Makefile` — fixed `lua5.1 -p` → `luac5.1 -p` (separate binary),
  93. rewrote lint rule with perl-based comment stripping + per-line
  94. feature scan, added `unit` target.
  95. ### Test Results
  96. ```
  97. make lint → OK (parse + forbidden-feature scan both clean)
  98. make unit → 137/137 PASS
  99. state → 8/8
  100. sha256 → 3/3
  101. routing → 32/32
  102. wg → 35/35
  103. ctl → 42/42
  104. smoke → 17/17
  105. end-to-end smoke (lua5.1 main.lua --dry-run):
  106. - boots clean, config loads, routing.apply_default prints dry-run cmds
  107. - status.json written: {"daemon":true,"state":"INIT","cycles":0,...}
  108. - balancerlite-ctl status/events/switch/compact/verify all functional
  109. - bad switch target (wan-z) correctly rejected
  110. ```
  111. ### Verify commands
  112. ```sh
  113. cd /root/.openclaw/workspace/balancer-lite-lua
  114. make lint # parse + 5.2+/5.3+/5.4 forbidden-feature scan
  115. make unit # all unit + smoke tests
  116. lua5.1 src/balancerlite/main.lua \
  117. --config etc/config/balancerlite.example --dry-run # daemon smoke
  118. lua5.1 src/balancerlite/ctl_main.lua \
  119. --state-dir /root/balancerlite -- status # CLI smoke
  120. ```
  121. ### Notes
  122. - Three bug categories caught while wiring (the wiring *is* the test
  123. surface for these): (1) Lua patterns treating `\0` as zero-width
  124. match (silent corruption of every JSON string field); (2) Lua's
  125. `X and Y or Z` short-circuit treating `false` as missing
  126. (silently flipping disabled bool options back to enabled default);
  127. (3) modules exporting functions only as locals so callers couldn't
  128. see them via the require'd module table. All three are *easy to
  129. write but hard to spot* — caught here by end-to-end boot attempts,
  130. not by per-module unit tests. Worth flagging as recurring footguns.
  131. - Routing/wg actuator design: separate *pure* (command builders,
  132. regex parsers, validators) from *effectful* (io.popen + rc-capture)
  133. via an injectable `exec` hook. Lets tests assert on the command
  134. list without requiring root or iproute2.
  135. - ctl <-> daemon IPC is file-based (`control.json` request, daemon
  136. polls each cycle, `status.json` written each cycle) to avoid Lua
  137. socket dependencies on OpenWrt's stripped-down Lua 5.1.
  138. ### Open loops
  139. - Real (non-dry-run) end-to-end test on `openwrt-testbed` (19.07.7
  140. x86_64 container, would need real `ip` + `wg` + `ip rule` to
  141. exercise the actuators fully).
  142. - Optional: procd SIGHUP hot-reload (config.lua has the fields but
  143. the daemon doesn't yet respond to SIGHUP).
  144. - Optional: signed-webhook outbox tests (currently only state is
  145. shipped; tests for HMAC envelope + retry queue were left as a
  146. follow-up since the pure outbox logic is exercised by the daemon
  147. already).