fix: genie api dashboard proxies to core port - #102
Conversation
ai-hpc
left a comment
There was a problem hiding this comment.
This is the right architectural fix for #99. PR #92 (the genie-ctl version of this bug) added the core_addr_from_config helper locally inside genie-ctl/src/main.rs; this PR correctly identifies that the same logic was hardcoded in genie-api's proxy_core_json, promotes the helper to a public Config::core_http_addr() on genie-common::config::Config, and threads it through both crates. Net -14 lines in genie-ctl because the duplicate goes away. That's the architectural cleanup PR #92 would have done if we'd known #99 was coming.
Substance
Config::core_http_addr()preserves the0.0.0.0/::→127.0.0.1mapping from PR #92 (local callers want loopback even when core listens on all interfaces). Emptybind_hostfalls back to127.0.0.1. Tests ingenie-common(core_http_addr_uses_bind_host_and_port,core_http_addr_maps_listen_all_to_loopback) pin both branches.proxy_core_jsonnow takesconfig: &Configas the first arg. Every caller (get_actuation_pending,get_actuation_actions,get_runtime_contract,post_actuation_confirm,get_memories,post_memory_update,post_memory_delete,post_memory_reorder) renames its_configparam toconfigand threads it through. 8 call sites, all consistent — easy to read, easy to grep.Host:header is now correct. Previously hardcodedHost: 127.0.0.1; now uses the actual host parsed fromaddr.rsplit_once(':'). Necessary because the dashboard could (in theory) talk to a non-loopback host, and getting theHost:header wrong breaks vhost-routing in any reverse proxy in front of genie-core.- Error messages include the dialed
host:port(format!("{addr}: {e}")). The previous opaque "Connection refused" left operators guessing which port the proxy thought it was hitting; now they see it immediately. Tiny but real DX win. core_proxy_addr_uses_configured_core_porttest ingenie-apisetsconfig.core.port = 3001and assertsconfig.core_http_addr() == "127.0.0.1:3001". Pins the contract.
End-to-end verification in the PR body is the strongest signal: same geniepod.toml for both daemons with port = 3001, then diff <(curl :3001/api/actuation/pending) <(curl :3080/api/actuation/pending) returns identical output. Before this PR, the second curl would have hung or errored. After, the proxy correctly routes to the configured port.
One small contributor note (not blocking)
The commit author email is andiry.polanski@outlook.com — a personal Outlook address rather than the GitHub-noreply form. That lands in git log permanently on a public AGPL repo where every clone / mirror picks it up. The fix is a one-time git config --global user.email "<numeric-id>+andriypolanski@users.noreply.github.com" (find the noreply form at https://github.com/settings/emails), then git commit --amend --reset-author + force-push.
I'm flagging this as a soft norm rather than a hard block because:
- The commit author login (
andriypolanski) matches the PR submitter — no account mismatch concern. - No AI co-author trailer in the commit message.
- The work is clearly correct and addresses a real production bug.
PR #105 was the strict-block case (account mismatch + Cursor trailer + personal Gmail — three compounding issues). This is just the email; not the same severity. Maintainer's call whether to land as-is and just note for next time, or hold for the amend.
CI
statusCheckRollup had only the PR body checklist: SUCCESS entry — the cargo / clippy / test / aarch64 cross-compile / --no-default-features jobs were waiting on first-time-contributor approval. I approved both queued workflow runs (CI: 26042476432, Cross-compile: 26042476465) so the full check matrix will populate on the current head.
Interaction with PR #105
PR #105 (fix/103-configured-service-http-probes, currently with a CHANGES_REQUESTED review) also touches genie-common/src/config.rs to add [services.api] and service_http_probe(). If both merge, there's no direct conflict (different functions, separate sections), and #105 actually could have used this PR's Config::core_http_addr() helper rather than duplicating the parsing logic in its own service_http_probe. Once #102 lands, a follow-up review of #105 should suggest harmonizing those two helpers (or at least cross-referencing them in the comments).
Verdict
Approving on substance. Waiting on CI to clear green on the new head before this is mergeable.
The two prior branches (empty host vs `0.0.0.0` / `::`) both returned `"127.0.0.1"`, which trips `clippy::if_same_then_else -D warnings` on the workspace lib. Combine into a single OR'd condition; behavior is byte-identical. No-op fix to unblock CI on PR GeniePod#102.
|
Merged at |
Summary
genie-apiproxies actuation, memories, and runtime-contract endpoints to genie-core via raw HTTP over TCP. Those proxies always dialed127.0.0.1:3000, ignoring[core].bind_hostand[core].portingeniepod.toml. With a non-default core port, the dashboard Services row for core could still look healthy (health checks use[services.core].url) while Actuation and Memories panels failed with connection errors.Closes #99
Related: #90 (fixed
genie-ctlonly)Changes
Config::core_http_addr()ingenie-common(map0.0.0.0/::to127.0.0.1for local clients; same semantics as the formergenie-ctlhelper).proxy_core_jsoningenie-apito connect using that address; connection errors include the dialedhost:port.genie-ctlto the shared helper so CLI and API stay aligned.genie-commonandgenie-api.Real Behavior Proof
Environment: x86_64 Ubuntu 24.04 VM (
Linux 6.17.0-23-generic), not Jetson. Equivalent path: samegeniepod.toml+genie-core/genie-apistack as production, with[core].port = 3001and llama.cpp on:8080(dev config).What I ran
What I observed
Finished release profileforgenie-common,genie-core,genie-api.starting HTTP chat API port=3001andgenie-core HTTP server listening addr=127.0.0.1:3001.listening addr="127.0.0.1:3080".curl …:3001/api/health→"status":"ok","llm":"connected".GET /api/actuation/pending→{"audit_log":{...},"pending":[]}on both;diff→ identical.GET /api/memories→[], HTTP 200 via:3080.GET /api/runtime/contract→ JSON withcontract_hash, HTTP 200 via:3080.curl …:3000/api/actuation/pending→ connection failed (HTTP 000) while:3080proxy succeeds — confirms proxies no longer assume port 3000.core_http_addr_*andproxy_core_uses_configured_core_http_addrpassed.Test plan
cargo test -p genie-common core_http_addrcargo test -p genie-api proxy_core_uses_configured_core_http_addrcargo test -p genie-ctl[core].port = 3001(x86 dev VM)http://127.0.0.1:3080/and confirm Actuation / Memories panels load with non-default core portNotes for reviewers
Config::core_http_addr()replaces duplicate logic ingenie-ctl(genie-ctl ignores[core].portandbind_host— always connects to127.0.0.1:3000#90) and fixes the remaining hardcode ingenie-api(genie-apidashboard proxies ignore[core].port— hardcode127.0.0.1:3000#99).[services.core].urlshould stay aligned with[core].portfor the Services table health row; this PR only fixes the TCP proxy path.Affected routes
GET /api/actuation/pendingGET /api/actuation/actionsPOST /api/actuation/confirmGET /api/runtime/contractGET /api/memoriesPOST /api/memories/update,/delete,/reorder