fix(gateway): apply dashboard JSON config and reload live settings - #99
Conversation
Dashboard PUT /api/config sent JSON while the handler wrote raw TOML and never swapped live provider/gateway settings. Patch known fields into config.toml and reload so GET reflects the save immediately. Refs: #58
Transport already rejects bodies over 64 KiB before routes run; keep that boundary under test so dashboard saves cannot truncate config. Refs: #56
There was a problem hiding this comment.
Summary
This is a solid, correctly scoped fix for the dashboard config save path. PUT /api/config was writing the JSON body as if it were TOML and never swapping live settings; this PR patches the five dashboard fields into config.toml, validates the result, then reloads so GET /api/config matches without SIGHUP. The shutdown double-free fix (stale_free_all then config_free(bootstrap_get_cfg())) is real, and the JSON path only touches known keys so a dashboard save cannot clobber sandbox or API-key settings.
I did not find a merge blocker on the happy path (config.example.toml layout + web/js/app.js payload). Locally CI=true make test_config_patch LDLIBS=-lm passed. This image has no libcurl/libwebsockets, so I did not run test_reload or GATEWAY=1 make test_gateway_http. GitHub static, test, release was still pending at review time.
Must Fix
None.
Should Fix
- The TOML patcher only matches column-0 keys and section headers whose next character is EOL. Indented keys (
model = ...) and[agent] # commentmiss the existing line, insert a duplicate, andtomlc99then fails withkey exists(HTTP 400). I reproduced both. Skip leading whitespace infind_key_lineand allow trailing space/comment after]infind_sectionso formatter-indented configs save in place. - Present-but-wrong-type JSON fields are ignored and the handler still returns 200.
{"model":123}is a successful no-op. If a key is present and not the expected JSON type, return 400 so a client cannot think the save applied. try_config_reloadnow runs on the libwebsockets thread.stale_enqueue/bootstrap_set_cfgare unlocked; a SIGHUP on main at the same time can enqueue the same pointer twice and double-free on shutdown. Holdagent_lock()around the swap (or marshal the reload onto the main loop and wait) so GET still reflects the save.- Add a CHANGELOG Unreleased Fixed note. This is a user-visible dashboard bugfix; neighboring gateway fixes are already listed there.
Nice to Have
handle_config_putis 82 lines androutes.cis 984 (the 1000-line rule is close). Extract JSON vs TOML PUT + reload into a helper.validate_patched_tomlwrites*.patch-test, then PUT writes*.tmpand validates again. Drop the first disk round-trip; keep the.tmp+config_loadgate.- Escape
//inescape_toml_stringinstead of failing closed on a model that contains a newline. test_api_config_put_jsonshould also asserttemperatureandgateway_hoston GET, plus a unit test for quote escaping (that path already works).- Gateway host/port still do not rebind (documented for SIGHUP in
main.c). A dashboard save ofgateway_portupdates GET while the process keeps the old listen port until restart. A UI note would avoid confusion.
Positive Highlights
- New
config_patchmodule instead of growingconfig.c(already under a 1000-line waiver). JSON merge is allowlisted to the five dashboard fields; unknown JSON keys cannot rewrite[sandbox]or provider secrets. Raw TOML PUT is unchanged. - Reload ownership is thought through: enqueue
bootstrap_get_cfg()rather than the caller pointer, extrahttp_set_live_configbecausetest_reloadbuilds withGATEWAY=0, and shutdown frees the live pointer afterstale_free_all.test_try_config_reload_ignores_stale_caller_pointercovers the actual double-free shape. - Tests match the bug: unit patch (update, insert, missing section, invalid JSON), HTTP JSON PUT then GET, and the leftover 64 KiB 413 from #56.
Sent by Cursor Automation: Adrianno鈥檚 personal code review
Formatter-indented keys and commented section headers were missed, so a dashboard save inserted a duplicate key and tomlc99 returned 400. A present field with the wrong JSON type now fails instead of a 200 no-op, and string values escape quotes and newlines.
Dashboard save and SIGHUP both swap the live config. Hold agent_lock on both paths so the same pointer cannot be queued twice, and return HTTP 500 when the file is saved but the live reload does not.
|
Should Fix from the review is in c0c7d82 and eedd31c (threads resolved). Nice to have:
|


Summary
development(not a cherry-pick of the main-based drafts).routes.calready includes cron dispatch from fix(cron): full TEXT, deferred ack, no same-minute re-fire聽#95; this only changeshandle_config_put.PUT /api/confignow detects JSON, patchesmodel/max_tokens/temperature/gateway_host/gateway_portintoconfig.toml, and reloads live settings (try_config_reload+http_set_live_config) so GET matches the save without SIGHUP.stale_free_allso an HTTP-thread reload cannot double-free main's stale pointer.PUT /api/configwith a body >64 KiB returns 413. Invalid TOML + valid TOML PUT were already ondevelopment.Supersedes #58 and #56.
Does not mix sandbox, file, or camera.
Test plan
make test_config_patch/./build/test_config_patchmake test_reload/./build/test_reloadGATEWAY=1 make test_gateway_http/./build/test_gateway_httpCI=true make testmake staticMade with Cursor