7.1 KiB
7.1 KiB
RPC Implementation (jsonrpsee) for Supervisor
Objective
- Provide an HTTP/WS JSON-RPC server with jsonrpsee that exposes all Supervisor job operations.
- Use the current Supervisor and job model directly; methods should map 1:1 to Supervisor APIs.
- Keep the implementation simple: a single transport (jsonrpsee::server::Server on SocketAddr).
Canonical model
- Jobs are stored and updated in Redis under hero:job:{job_id}.
- Work is dispatched to type queues hero:q:work:type:{script_type}.
- Actors consume by script type and update the job hash status/output.
- Server-side types and queues are already aligned in code (see keys in rust.keys).
What exists today (summary)
- Server state and registry
- rust.OpenRpcServer holds a Supervisor inside RwLock.
- Methods are registered manually with jsonrpsee::RpcModule in rust.OpenRpcServer::start().
- Methods wired vs. stubbed
- Wired: create_job, start_job, get_job_status, get_job_output, stop_job, delete_job, clear_all_jobs.
- Stubbed or partial: run_job (returns a formatted string), play (returns canned output), get_job_logs (mocked), list_jobs (returns fabricated Job objects from IDs).
- Transports
- start() supports a Unix transport through reth-ipc and a WebSocket SocketAddr. We only need HTTP/WS via jsonrpsee::server::Server::builder().build(addr).
Target surface (final)
- Methods
- fetch_nonce(pubkey: String) -> String [optional now]
- authenticate(pubkey: String, signature: String, nonce: String) -> bool [optional now]
- whoami() -> String [optional now]
- play(script: String) -> PlayResult { output: String } [maps to run_job with a chosen default ScriptType]
- create_job(job: JobParams) -> String (job_id)
- start_job(job_id: String) -> { success: bool }
- run_job(script: String, script_type: ScriptType, prerequisites?: Vec) -> String (output)
- get_job_status(job_id: String) -> JobStatus
- get_job_output(job_id: String) -> String
- get_job_logs(job_id: String) -> JobLogsResult { logs: String | null }
- list_jobs() -> Vec
- stop_job(job_id: String) -> null
- delete_job(job_id: String) -> null
- clear_all_jobs() -> null
- Types
- ScriptType = OSIS | SAL | V | Python (rust.ScriptType)
- JobParams: script, script_type, caller_id, context_id, timeout?, prerequisites?
- JobStatus: Dispatched | WaitingForPrerequisites | Started | Error | Finished
- DTOs in rust.interfaces/openrpc/server/src/types.rs
Required changes
- Transport: simplify to HTTP/WS on SocketAddr
- Remove Unix transport: in rust.OpenRpcServer::start(), delete Transport::Unix and reth-ipc usage.
- Use jsonrpsee::server::Server::builder().build(addr) and server.start(module), per upstream examples:
- ScriptType consistency end-to-end
- Ensure ScriptType is hero_job::ScriptType (OSIS | SAL | V | Python) in request/response types (already used in rust.JobParams). If openrpc.json is used to generate docs or clients, update its enum to match.
- Implement run_job (one-shot)
- In rust.OpenRpcApiServer::run_job:
- Build a hero_job::JobBuilder with caller_id/context_id placeholders (or accept them as parameters later).
- Set script, script_type, optional prerequisites, timeout default.
- Call supervisor.run_job_and_await_result(&job) and return the output string.
- Implement play as a thin wrapper
- In rust.OpenRpcApiServer::play:
- Choose a default ScriptType (recommendation: SAL), then delegate to run_job(script, SAL, None).
- Return PlayResult { output }.
- Implement get_job_logs via Supervisor
- Replace the mocked return in rust.get_job_logs with a call to:
- supervisor.get_job_logs(&job_id) -> Option and wrap into JobLogsResult { logs }.
- list_jobs should return Vec (IDs only)
- Replace placeholder construction in rust.list_jobs with:
- supervisor.list_jobs() returning Vec directly.
- Optionally add get_job(job_id) later if needed.
- Error handling
- Map SupervisorError to jsonrpsee error codes:
- Invalid input → ErrorCode::InvalidParams
- Timeout → a custom code or InvalidParams; optionally use -32002 as a custom timeout code.
- Internal IO/Redis errors → ErrorCode::InternalError
- Keep server logs descriptive; return minimal error messages to clients.
- Server lifecycle
- Keep OpenRpcServer::new() to build with TOML or builder defaults (see rust.OpenRpcServer::new()).
- Expose a “start_on(addr)” function that returns a ServerHandle (just like upstream examples).
- Optional: expose Supervisor::start_rpc_server(host, port) to own lifecycle from Supervisor; or leave it in interfaces/openrpc with a thin cmd binary to start it.
Non-goals (for this phase)
- Unix IPC transport (reth-ipc).
- Advanced middleware (CORS, host filters, rate-limiting).
- RPC auth flows (fetch_nonce/authenticate/whoami) beyond placeholders.
- Pub/Sub over RPC.
Reference mapping (clickable)
- Server core and methods:
- Supervisor backend:
- rust.Supervisor::create_job()
- rust.Supervisor::start_job()
- rust.Supervisor::run_job_and_await_result()
- rust.Supervisor::get_job_status()
- rust.Supervisor::get_job_output()
- rust.Supervisor::get_job_logs()
- rust.Supervisor::list_jobs()
- rust.Supervisor::stop_job()
- rust.Supervisor::delete_job()
- rust.Supervisor::clear_all_jobs()
- jsonrpsee examples to replicate transport and registration patterns:
- HTTP: rust.http example
- WS: rust.ws example
Acceptance checklist
- Server starts on a host:port using jsonrpsee::server::Server.
- All Supervisor operations callable over RPC, 1:1 mapping, returning correct DTOs.
- ScriptType uses OSIS|SAL|V|Python.
- list_jobs returns Vec and no fake job objects.
- run_job and play perform real execution and return actual outputs.
- No Unix IPC code path remains in start().