Adapt hero_proc, hero_inspector, and hero_proxy to the hero_sdk architecture (hero_rpc#13) #34
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_proc#34
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?
Context
hero_rpc is being restructured into hero_sdk and hero_osis into hero_core (see hero_rpc#13). The key architectural changes that affect the infrastructure services (hero_proc, hero_inspector, hero_proxy) are:
_serverand_uibinaries. One binary runs everything.hero_sdk_modelsprovides feature-gated domains (identity, communication, calendar, etc.) that any service can import.This issue explores what each infrastructure service needs to change.
1. hero_proc — Process Supervisor
Current state
hero_*_serverandhero_*_ui(two binaries, two PIDs)install_and_run.rhai) register and start two services per projectquick_service_set_full(name, cmd, "exec", "")for each binaryWhat changes
a) Single service registration instead of two
With the unified binary, hero_proc registers one service per project:
Instead of:
The single binary creates all three socket types on startup. hero_proc only needs to manage one process.
b) Service health should check all three socket layers
Currently hero_proc pings one socket per service. With the new architecture:
{service}.sock) — basic health, context management{service}_ui.sock) — HTTP health check{service}_server/{context}/{domain}.sock) — per-domain healthhero_proc's health model should report:
c) Rhai scripts simplify
Current pattern (two services):
New pattern (one service):
d) Domain-level operations
hero_proc could expose domain-aware operations:
service.domains("hero_food")— list active domains and contextsservice.domain_health("hero_food", "root", "delivery")— health check a specific domaine) Context lifecycle integration
hero_core manages context lifecycle (create/delete). hero_proc should be aware of context changes so it can:
2. hero_inspector — Service Discovery & Documentation
Current state
~/hero/var/sockets/for.sockfileshero_food_serverandhero_food_uiare separate entriesWhat changes
a) Hierarchical service discovery
Instead of flat socket scanning, inspector should understand the service → socket hierarchy:
The autodiscovery list shows one entry per service, with sub-navigation for domains.
This aligns with the already-requested changes in hero_inspector#10 — unified entries, embedded UI tab, etc.
b) Aggregate OpenRPC per service
Each domain socket has its own OpenRPC spec. Inspector should:
hero_sdk_models(shared) vs customc) Context-aware views
Inspector should show:
d) Service info socket
The
{service}.sockprovides metadata about the service:Inspector should read this to populate the service header.
3. hero_proxy — HTTP Reverse Proxy
Current state
/{service_name}→~/hero/var/sockets/{service_name}.sockhttp://127.0.0.1:9998/hero_food_ui→hero_food_ui.sockhttp://127.0.0.1:9998/hero_food_server→hero_food_server.sockWhat changes
a) Hierarchical URL routing
With three socket types, the proxy should support structured URL patterns:
Examples:
b) Backward compatibility
The current flat routing should continue to work during migration:
c) Per-domain access control
The hierarchical socket model enables fine-grained access:
/{service}/{context}/delivery/rpcThis is particularly powerful for the agent use case mentioned in hero_rpc#13: "give AI agent only the domain socket it needs."
d) Context routing
Proxy needs to understand that
{context}is a variable path segment, not a static service name. Discovery of valid contexts could come from:~/hero/var/sockets/{service}_server/subdirectories4. Cross-cutting concerns
a) Socket discovery convention
All three infrastructure services need a shared understanding of the socket layout. Consider a
hero_sdk_discoverycrate or convention that:discover_services()function that returns the full service treeb) Service manifest
Each service's
{service}.sockshould serve a standardized discovery manifest atGET /.well-known/heroservice.json(this already exists in hero_rpc). It should include:This manifest is the single source of truth that hero_proc, hero_inspector, and hero_proxy all consume.
c) Transition plan
During migration, both architectures will coexist:
_server+_ui)All infrastructure services must handle both patterns gracefully. The service manifest (or its absence) can be used to distinguish old vs new services.
Summary
References
Tradeoff analysis: single binary vs separate server/UI processes
What's clearly better with single binary
HeroServer::new("hero_food").with_ui(router()).run()is ~15 lines vs the current 5-crate boilerplate.What we lose
hero_food_uiwithout touchinghero_food_server. Useful when iterating on the dashboard without disrupting running services or connected clients. With a single binary, a UI fix requires full service restart.Assessment
The losses are real but minor for our context. Everything runs on a single machine, services are small, and the operational pain of managing two processes per service across 20+ services is the dominant cost. The fault isolation argument is the strongest counterpoint, but Rust services rarely panic, and the UI is just an Axum router — not a complex separate system.
The per-domain per-context sockets are the real architectural win here. That's a much more meaningful boundary than server-vs-UI. It gives proper multi-tenancy isolation, agent-scoped access, and focused OpenRPC specs — things the current architecture can't do.
Importantly, even with a single binary, the three socket types mean hero_proc can still do socket-level health checks independently. If the UI socket stops responding but domain sockets are healthy, hero_proc can report that. We don't need separate processes to get separate health signals — we just need separate endpoints, which the new architecture provides.
Verdict: Better architecture, minor tradeoffs, and the things we lose can mostly be recovered through socket-level monitoring rather than process-level separation.
not sure I agree about one process,
we don't want to have _ui in memory if not used
this is only needed when a UI process needs it
and over time, many of them will become webassembly only ones
this also makes sure we have to over over openrpc & sdk
there is no duplicate runtime, the opposite, the UI is only there if needed
otherwise you load all the assets, ...