Unify server lifecycle: all Hero services should use one pattern #27
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Hero services currently use four different patterns to start up and manage their lifecycle:
OServer::run_cli()OsisAppRpcHandler+OsisDomainInittraitsZinitLifecycle+ manual CLI dispatchrun_cli()This fragmentation means:
run,start,stop,status,logs)/health,/.well-known/heroservice.json,/openrpc.json,POST /rpc) are implemented inconsistentlyOServer::run_cli()can't be used by non-OSIS services because it requires domain-specific traitsRoot Cause
OServerconflates two concerns:Every Hero service needs (1). Only OSIS services need (2). But because they're fused together in
OServer, non-OSIS services can't use the standard lifecycle.Proposed Architecture
Core Principle
All Hero servers are the same thing: a process that binds an Axum router to a Unix socket and serves JSON-RPC over HTTP. The only differences are the routes they register and any background tasks they run.
Solution:
HeroServerinhero_rpcIntroduce a
HeroServerbuilder (inhero_rpc::serveror a newhero_rpc_servicecrate) that every service uses:HeroServer::run()handles everything:LifecycleCommand(run/start/stop/status/logs/ui/zinit) +serveZinitLifecyclefor lifecycle commands/health,/.well-known/heroservice.json,/openrpc.json~/hero/var/sockets/{service_name}.sock--socket-dirplus service-specific args via.serve_args::<T>()OServer becomes a HeroServer extension
OServercontinues to exist but builds onHeroServerinternally, adding OSIS-specific domain management:Migration Path
For each service, the migration is:
HeroServer::new(...).router(existing_router).run()/healthand discovery endpoint handlers (auto-injected)Scope
Services to migrate (in priority order):
Deliverables
HeroServerbuilder inhero_rpc(orhero_rpc_service)hero_rpc_server_lifecycle,hero_service,hero_openrpc_server_admin_template)home/docs/with unified architecture docsZinitLifecycle+ manual CLI pattern in documentationImplementation Plan
Architecture
New
HeroServerbuilder inhero_servicecrate (hero_rpc repo). Every Hero service uses it:HeroServer::run()handles: CLI parsing (LifecycleCommand + Serve), zinit lifecycle dispatch, mandatory endpoint injection (/health, /.well-known/heroservice.json, /openrpc.json), Unix socket binding, optional TCP binding, graceful shutdown.API Variants
run::<A>(|args| -> Router)— standard: CLI + lifecycle + serverun_simple(|| -> Router)— no custom serve argsrun_raw::<A>(|args| -> ())— CLI + lifecycle, service manages own sockets (for OServer)serve(|| -> Router)— no CLI parsing, just bind+servelifecycle() -> ZinitLifecycle— for manual dispatch (services with extra CLI commands)Steps
hero_rpc/crates/service— newhero_server.rsmodulehero_inspector_server— usesserve()+lifecycle()(has custom CLI subcommands)hero_inspector_ui— usesrun()withtcp_bind()home/docs/architecture/hero-server.mdhero_rpc_server_lifecycle,hero_service,hero_openrpc_server_admin_templateBranches
All repos:
development_home27Progress Update
Completed
HeroServerbuilder implemented inhero_rpc/crates/service(branchdevelopment_home27)hero_inspector_servermigrated — lifecycle commands + socket binding via HeroServerhero_inspector_uimigrated — lifecycle commands + UDS/TCP binding via HeroServerhome/docs/architecture/hero-server.mdhero_rpc_server_lifecycle— HeroServer is now primary patternBranches pushed
hero_rpc→development_home27hero_inspector→development_home27home→development_home27hero_skills→development_home27What changed
hero_inspector_server: -263 lines, +139 lines (removed manual hyper/socket binding, added lifecycle commands)hero_inspector_ui: removed manual socket accept loop, added lifecycle + graceful shutdownrun/start/stop/status/logs/ui/zinitcommands via zinitRemaining (future PRs)
Refinement: Simplify HeroServer
After initial implementation, several simplifications identified:
Changes
Remove TCP binding — Hero standardizes on Unix sockets only.
hero_proxyhandles TCP→UDS. No.tcp_bind()on HeroServer.Remove lifecycle-only fields from HeroServer —
repo_url,package_name,branchbelong onZinitLifecycle, not on the server builder. HeroServer just runs servers. Lifecycle config is separate.Remove
version— derivable from openrpc spec orCARGO_PKG_VERSION. Not a server concern.Derive socket path from name — always
~/hero/var/sockets/{service_name}.sock. Remove.socket_path()override.Replace
skip_mandatory_endpoints()with two server types:HeroRpcServer— requires openrpc spec, injects /health + /openrpc.json + /.well-known/heroservice.jsonHeroUiServer— for UI crates whosebuild_router()already provides everythingFuture (separate issue)
hero_rpc_serverintohero_serviceImplementing now
Simplifying HeroServer on the existing
development_home27branches.Simplification Complete
Refactored HeroServer into two distinct types:
HeroRpcServer— requires OpenRPC spec (2nd arg), auto-injects/health,/openrpc.json,/.well-known/heroservice.json, extracts version from specHeroUiServer— no mandatory endpoints, for UI crates whosebuild_router()provides everythingRemoved
repo_url,package_name,branch(lifecycle-only fields, stay on ZinitLifecycle)versionfield (derived from OpenRPC spec)socket_pathoverride (convention:~/hero/var/sockets/{service_name}.sock)skip_mandatory_endpoints()(replaced by server type choice)Usage
All branches updated and pushed.
hero_osis Migration Progress
Completed
dispatch_jsonrpc_auto_context()— extracts_contextfrom JSON-RPC params (defaults to "root"), strips before forwarding. Also addsrpc.contextsmethod.hero_osis_servermigrated toHeroRpcServer— single socket, HTTP/1.1 transport, lifecycle commandsArchitecture Change
Before: N sockets (one per context), raw newline-delimited JSON-RPC
After: 1 socket, HTTP over UDS, context in
_contextparamRemaining
hero_osis_ui→HeroUiServer(proxy needs updating for HTTP backend)Branches
hero_rpc→development_home27(dispatch_jsonrpc_auto_context)hero_osis→development_home27(server migration)hero_osis Migration Complete
All components migrated:
dispatch_jsonrpc_auto_context()— extracts_contextfrom params, addsrpc.contextshero_osis_server→HeroRpcServer— single socket, HTTP/1.1, context in paramshero_osis_ui→HeroUiServer— lifecycle commands, HTTP proxy to single backend socketArchitecture (before → after)
All repos on
development_home27:hero_rpc— HeroRpcServer, HeroUiServer, dispatch_jsonrpc_auto_contexthero_inspector— server + ui migratedhero_osis— server + ui migratedhome— architecture docshero_skills— skill updatesNote
hero_osis_server has pre-existing compilation issues in generated
osis_server_generated.rsfiles (unrelated to this migration). These need regeneration from the updated hero_rpc_osis branch.Remaining Work & Next Steps
Migrate remaining services
hero_fossil→HeroRpcServerhero_redis→HeroRpcServerhero_embedder→HeroRpcServerhero_books→HeroRpcServerhero_indexer→HeroRpcServerhero_aibroker→HeroRpcServerhero_proxy→HeroRpcServerDeprecate OServer (hero_os)
OServeris now redundant — its responsibilities are split:HeroRpcServerUnixRpcServer/ServerState(reusable directly)hero_os_servershould migrate toHeroRpcServer+ServerStatefor domain registration (same pattern as hero_osis). Thehero_rpc_servercrate's OServer can then be deprecated.WASM UI servers (hero_os_ui, hero_archipelagos)
HeroUiServer works for WASM apps too — the router just serves different static assets (WASM binary + JS glue vs Askama templates). No separate server type needed.
Regenerate hero_osis generated code
The
osis_server_generated.rsfiles need regeneration after switching todevelopment_home27branch ofhero_rpc_osis. This is a pre-existing issue, not migration-related.See #28 for standard testing and
hero_serviceCLI tooling.Switch from zinit to hero_init
Per Kristof's direction: zinit is being split into:
geomind_code/my_init) — simplified, back to basics for zos/moslhumina_code/hero_init) — current zinit with advanced features, for Hero OS. No TOML format, all OpenRPC.What needs to change in hero_rpc
hero_service/src/lifecycle.rscurrently depends onzinit_sdk. The migration is a rename:zinit_sdkcrate dephero_init_sdkzinit_sdk::ZinitRPCAPIClienthero_init_sdk::HeroInitRPCAPIClient(or similar)zinit_sdk::ServiceBuilderhero_init_sdk::ServiceBuilderzinit_sdk::ActionBuilderhero_init_sdk::ActionBuilderzinit_server.sockhero_init_server.sockZINIT_SOCKETHERO_INIT_SOCKETThe API surface stays the same — same methods (
service_start,service_stop,service_status,logs_get,job_create,action_set).Steps
lhumina_code/hero_init— fork of zinit, rename crateshero_service/Cargo.toml— swapzinit_sdk→hero_init_sdkhero_service/src/lifecycle.rs— swap types/importsBlocked on
hero_init Migration Complete
What was done
geomind_code/zinit→lhumina_code/hero_inithero_init_sdkinstead ofzinit_sdkZinitLifecycle→HeroLifecycleZINIT_SOCKET→HERO_INIT_SOCKETzinit_server.sock→hero_init_server.sockzinitsubcommand →hero-initRepos & branches
lhumina_code/hero_init→development_home27(full rename, compiles clean)lhumina_code/hero_rpc→development_home27(switched to hero_init_sdk)Downstream impact
All services using
hero_serviceget the change automatically. They importHeroLifecycle(or use it viaHeroRpcServer/HeroUiServer). No changes needed in service repos — the rename is internal to hero_service.The hero_inspector and hero_osis migrations on
development_home27will needcargo updateto pick up the new hero_rpc commit.