bug: Hardcoded CamelCase RPC method names fail with AxumRpcServer #80

Closed
opened 2026-04-07 12:51:18 +00:00 by mahmoud · 3 comments
Owner

Problem

AxumRpcServer registers methods in lowercase (e.g. computeservice.get_vm), but several places in the codebase use CamelCase (e.g. ComputeService.get_vm) in hardcoded RPC calls. These calls silently return "Method not found" errors.

The UI RPC proxy was fixed (v0.1.6) by adding lowercase_method() to the /rpc and /explorer/rpc proxy handlers. But there are other call sites that bypass the proxy and construct RPC bodies directly — these are still broken.

Affected Locations

crates/hero_compute_ui/src/server.rs

Line Method Used By Impact
454 ComputeService.node_status /status health check handler Health check silently fails — server always shows "unavailable"
824 explorer.ExplorerService.get_vm Console WebSocket handler (explorer mode) Console 403 Forbidden in explorer mode
852 ComputeService.get_vm Console WebSocket handler (local mode) Console 403 Forbidden — "Method not found" returned as 403

crates/hero_compute_ui/static/js/dashboard.js

The browser JS sends CamelCase methods (ComputeService.node_status, ComputeService.deploy_vm, etc.) through the /rpc proxy. This is already fixed by lowercase_method() in the proxy handler. No JS changes needed.

Other crates to audit

Search for any hardcoded "method": with CamelCase in:

  • crates/hero_compute_server/src/ — heartbeat sender constructs RPC bodies
  • crates/hero_compute_explorer/src/ — proxy constructs RPC bodies
  • crates/hero_compute_examples/ — example code
  • crates/hero_compute/src/ — CLI

Symptoms

  • Console: Clicking console on a running VM returns 403 Forbidden with "Method not found"
  • Health check: /status endpoint may report server as unavailable even when it's running
  • Any inline RPC call: Returns empty or error response

Fix

Two approaches (pick one):

Option A: Lowercase all hardcoded method strings

Find-and-replace all hardcoded method names to lowercase:

// Before
"method": "ComputeService.get_vm"
// After  
"method": "computeservice.get_vm"

Simple but fragile — future code might reintroduce CamelCase.

Move lowercase_method() into http_rpc_unix() and http_rpc_tcp() so ALL RPC calls are automatically lowercased. No caller needs to worry about case.

pub async fn http_rpc_unix(socket_path: &str, rpc_path: &str, body: &str) -> Result<String, std::io::Error> {
    let body = lowercase_method(body);  // Always lowercase before sending
    // ... rest of function
}

This is the safest approach — it handles the JS proxy, inline Rust calls, and any future code.

Acceptance Criteria

  • Console works in local mode (no 403)
  • Console works in explorer mode (no 403)
  • /status health check returns correct server status
  • All hardcoded RPC calls use correct case
  • Grep for "method".*"[A-Z] in server.rs returns zero matches
  • Future RPC calls are automatically handled (Option B)
  • v0.1.6 partially fixed this for the /rpc proxy only
  • Same root cause: AxumRpcServer migration changed method dispatch from case-insensitive to case-sensitive
## Problem AxumRpcServer registers methods in **lowercase** (e.g. `computeservice.get_vm`), but several places in the codebase use **CamelCase** (e.g. `ComputeService.get_vm`) in hardcoded RPC calls. These calls silently return "Method not found" errors. The UI RPC proxy was fixed (v0.1.6) by adding `lowercase_method()` to the `/rpc` and `/explorer/rpc` proxy handlers. But there are **other call sites** that bypass the proxy and construct RPC bodies directly — these are still broken. ## Affected Locations ### `crates/hero_compute_ui/src/server.rs` | Line | Method | Used By | Impact | |------|--------|---------|--------| | 454 | `ComputeService.node_status` | `/status` health check handler | Health check silently fails — server always shows "unavailable" | | 824 | `explorer.ExplorerService.get_vm` | Console WebSocket handler (explorer mode) | Console 403 Forbidden in explorer mode | | 852 | `ComputeService.get_vm` | Console WebSocket handler (local mode) | Console 403 Forbidden — "Method not found" returned as 403 | ### `crates/hero_compute_ui/static/js/dashboard.js` The browser JS sends CamelCase methods (`ComputeService.node_status`, `ComputeService.deploy_vm`, etc.) through the `/rpc` proxy. This is **already fixed** by `lowercase_method()` in the proxy handler. No JS changes needed. ### Other crates to audit Search for any hardcoded `"method":` with CamelCase in: - `crates/hero_compute_server/src/` — heartbeat sender constructs RPC bodies - `crates/hero_compute_explorer/src/` — proxy constructs RPC bodies - `crates/hero_compute_examples/` — example code - `crates/hero_compute/src/` — CLI ## Symptoms - **Console**: Clicking console on a running VM returns `403 Forbidden` with "Method not found" - **Health check**: `/status` endpoint may report server as unavailable even when it's running - **Any inline RPC call**: Returns empty or error response ## Fix Two approaches (pick one): ### Option A: Lowercase all hardcoded method strings Find-and-replace all hardcoded method names to lowercase: ```rust // Before "method": "ComputeService.get_vm" // After "method": "computeservice.get_vm" ``` Simple but fragile — future code might reintroduce CamelCase. ### Option B: Lowercase at the transport layer (recommended) Move `lowercase_method()` into `http_rpc_unix()` and `http_rpc_tcp()` so ALL RPC calls are automatically lowercased. No caller needs to worry about case. ```rust pub async fn http_rpc_unix(socket_path: &str, rpc_path: &str, body: &str) -> Result<String, std::io::Error> { let body = lowercase_method(body); // Always lowercase before sending // ... rest of function } ``` This is the safest approach — it handles the JS proxy, inline Rust calls, and any future code. ## Acceptance Criteria - [ ] Console works in local mode (no 403) - [ ] Console works in explorer mode (no 403) - [ ] `/status` health check returns correct server status - [ ] All hardcoded RPC calls use correct case - [ ] Grep for `"method".*"[A-Z]` in `server.rs` returns zero matches - [ ] Future RPC calls are automatically handled (Option B) ## Related - v0.1.6 partially fixed this for the `/rpc` proxy only - Same root cause: AxumRpcServer migration changed method dispatch from case-insensitive to case-sensitive
Author
Owner

Recommendation: Option B — lowercase in the transport layer

Move lowercase_method() into http_rpc_unix() and http_rpc_tcp() in the SDK. Every RPC call goes through these two functions, so this is a single fix that covers:

  • The UI RPC proxy (/rpc, /explorer/rpc)
  • Inline RPC calls in the console handler, health check, etc.
  • Any future code that constructs RPC bodies

Once this is in the SDK, remove lowercase_method() from the UI's rpc_proxy handler — it becomes redundant.

Cleanup scope

While fixing this, also clean up outdated code from the AxumRpcServer migration:

  1. Remove SERVER_DOMAIN constant ("cloud") from the SDK — no longer used. The domain was needed by prepend_domain_to_method() which is already deleted.
  2. Remove any remaining "cloud." or "explorer." prefixes in hardcoded method strings — the domain is in the URL path, not the method name.
  3. Remove the lowercase_method() function from server.rs after moving it to the SDK transport layer.
  4. Audit crates/hero_compute_server/src/heartbeat_sender.rs — the heartbeat RPC body uses method names that may need lowercasing.
  5. Audit crates/hero_compute_explorer/src/explorer/proxy.rs — the explorer proxy also constructs RPC bodies.
  6. Fix the console handler's error mapping — "Method not found" should not return 403 Forbidden. Return 502 Bad Gateway or 500 Internal Server Error instead. 403 implies an auth failure which is misleading.

Goal: after this issue is closed, there should be zero CamelCase method strings in Rust code, and the transport layer guarantees lowercase for any future calls.

## Recommendation: Option B — lowercase in the transport layer Move `lowercase_method()` into `http_rpc_unix()` and `http_rpc_tcp()` in the SDK. Every RPC call goes through these two functions, so this is a single fix that covers: - The UI RPC proxy (`/rpc`, `/explorer/rpc`) - Inline RPC calls in the console handler, health check, etc. - Any future code that constructs RPC bodies Once this is in the SDK, **remove** `lowercase_method()` from the UI's `rpc_proxy` handler — it becomes redundant. ## Cleanup scope While fixing this, also clean up outdated code from the AxumRpcServer migration: 1. **Remove `SERVER_DOMAIN` constant** (`"cloud"`) from the SDK — no longer used. The domain was needed by `prepend_domain_to_method()` which is already deleted. 2. **Remove any remaining `"cloud."` or `"explorer."` prefixes** in hardcoded method strings — the domain is in the URL path, not the method name. 3. **Remove the `lowercase_method()` function from `server.rs`** after moving it to the SDK transport layer. 4. **Audit `crates/hero_compute_server/src/heartbeat_sender.rs`** — the heartbeat RPC body uses method names that may need lowercasing. 5. **Audit `crates/hero_compute_explorer/src/explorer/proxy.rs`** — the explorer proxy also constructs RPC bodies. 6. **Fix the console handler's error mapping** — "Method not found" should not return `403 Forbidden`. Return `502 Bad Gateway` or `500 Internal Server Error` instead. 403 implies an auth failure which is misleading. Goal: after this issue is closed, there should be zero CamelCase method strings in Rust code, and the transport layer guarantees lowercase for any future calls.
mahmoud self-assigned this 2026-04-08 07:42:57 +00:00
mahmoud added this to the ACTIVE project 2026-04-08 07:43:00 +00:00
mahmoud added this to the now milestone 2026-04-08 07:43:03 +00:00
Author
Owner

Implementation Spec for Issue #80

Objective

Fix CamelCase RPC method names that cause "Method not found" errors by moving lowercase_method() into the SDK transport layer.

Implementation Plan

Step 1: SDK transport layer (hero_compute_sdk/src/lib.rs)

  • Add normalize_rpc_method() helper that parses JSON body, lowercases the method field, strips cloud./explorer. prefixes
  • Call it in both http_rpc_unix() and http_rpc_tcp() before sending
  • Remove SERVER_DOMAIN constant

Step 2: UI server (hero_compute_ui/src/server.rs)

  • Remove lowercase_method() function and its 2 call sites in proxy handlers
  • Fix hardcoded CamelCase: ComputeService.node_statuscomputeservice.node_status, etc.
  • Fix error mapping: 403 → 502 for RPC errors in console handler

Step 3: Heartbeat sender (hero_compute_server/src/heartbeat_sender.rs)

  • Normalize explorer.ExplorerService.node_heartbeatexplorerservice.node_heartbeat

Step 4: Explorer proxy (hero_compute_explorer/src/explorer/rpc.rs)

  • Remove cloud.ComputeService. prefix → computeservice.

Step 5: Integration tests (hero_compute_examples/tests/integration.rs)

  • Update all method strings to lowercase without domain prefix

Acceptance Criteria

  • Zero CamelCase method strings in Rust code
  • Console works in local and explorer mode (no 403)
  • /status health check returns correct status
  • cargo build --workspace succeeds
  • cargo test --workspace passes
## Implementation Spec for Issue #80 ### Objective Fix CamelCase RPC method names that cause "Method not found" errors by moving `lowercase_method()` into the SDK transport layer. ### Implementation Plan **Step 1: SDK transport layer** (`hero_compute_sdk/src/lib.rs`) - Add `normalize_rpc_method()` helper that parses JSON body, lowercases the `method` field, strips `cloud.`/`explorer.` prefixes - Call it in both `http_rpc_unix()` and `http_rpc_tcp()` before sending - Remove `SERVER_DOMAIN` constant **Step 2: UI server** (`hero_compute_ui/src/server.rs`) - Remove `lowercase_method()` function and its 2 call sites in proxy handlers - Fix hardcoded CamelCase: `ComputeService.node_status` → `computeservice.node_status`, etc. - Fix error mapping: 403 → 502 for RPC errors in console handler **Step 3: Heartbeat sender** (`hero_compute_server/src/heartbeat_sender.rs`) - Normalize `explorer.ExplorerService.node_heartbeat` → `explorerservice.node_heartbeat` **Step 4: Explorer proxy** (`hero_compute_explorer/src/explorer/rpc.rs`) - Remove `cloud.ComputeService.` prefix → `computeservice.` **Step 5: Integration tests** (`hero_compute_examples/tests/integration.rs`) - Update all method strings to lowercase without domain prefix ### Acceptance Criteria - [ ] Zero CamelCase method strings in Rust code - [ ] Console works in local and explorer mode (no 403) - [ ] `/status` health check returns correct status - [ ] `cargo build --workspace` succeeds - [ ] `cargo test --workspace` passes
Author
Owner

Implementation committed: c1032bb

Browse: c1032bb

Branch: development_fix_rpc_case — ready for PR to development.

Implementation committed: `c1032bb` Browse: https://forge.ourworld.tf/lhumina_code/hero_compute/commit/c1032bb Branch: `development_fix_rpc_case` — ready for PR to development.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_compute#80
No description provided.