Wire hero_proc_log + HERO_SOCKET_DIR + 0660 perms + hero_context claim auth #13
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
Several cross-cutting Hero primitives are not wired:
tracing_subscriberto stderr only;hero_proc_logrequires emitting structured logs viahero_proc_sdkwith named sources (hero_livekit_server,hero_livekit_ui).crates/hero_livekit_ui/src/main.rs:34-45andcrates/hero_livekit_examples/examples/health.rs:12hardcodehero/var/sockets/hero_livekit/...instead of theHERO_SOCKET_DIRenv-var cascade required byhero_sockets§7.1.0660perms are never set (required byhero_sockets§2).hero_sockets§7.2 providesbind_unix_socket.X-Hero-Context/X-Hero-Claimsheaders are read or enforced;herolib_openrpc_authorizeis unused.Goals
hero_proc_logsources for both binaries.HERO_SOCKET_DIR→$HOME/hero/var/sockets/cascade.0660permissions after bind on every UDS.bind_unix_sockethelper.X-Hero-Context+X-Hero-Claimsin RPC handlers; apply claim-based authorization patterns fromherolib_openrpc_authorizefor any privileged methods.Related skills:
hero_proc_log,hero_sockets,hero_context,herolib_openrpc_authorize.Implementation Spec for Issue #13 — Hero primitives wiring
Objective
Wire the four Hero cross-cutting primitives (
hero_proc_log,hero_sockets,hero_context,herolib_openrpc_authorize) intohero_livekit_serverandhero_livekit_ui. Replace the binaries' stderr-onlytracing_subscriberinitialisation withhero_proc_sdk::HeroLoggersources (hero_livekit_server,hero_livekit_ui). Replace the UI's hardcoded~/hero/var/sockets/hero_livekit/...paths and its customUnixListener/UnixStreamaccept loop withHERO_SOCKET_DIR-aware path resolution and the standardbind_unix_sockethelper fromhero_sockets§7.2, including the mandatory0660chmod after bind. ReadX-Hero-Context/X-Hero-Claimsin the UI proxy and forward them untouched torpc.sock; in the server, wrapOsisLivekitwithAuthorizedOsisLivekitto applyherolib_openrpc_authorize-style claim-pattern matching for privilegedLiveKitService.*methods while leaving read/join methods open to any context.Requirements
hero_livekit_server,hero_livekit_ui) viahero_proc_sdk::HeroLogger::new("…").await?HERO_SOCKET_DIRresolution perhero_sockets§7.1 — env var wins, else$HOME/hero/var/sockets0660perms perhero_sockets§2 on every UDS (serverrpc.sockalready gets this fromhero_rpc_server::unified_server; UIui.sockdoes NOT — must add)bind_unix_sockethelper perhero_sockets§7.2 replaces the UI's customserve_unixaccept loopX-Hero-Context+X-Hero-Claimsread by the UI proxy and forwarded intact, and enforced on privileged methods by the server viaherolib_openrpc_authorizepattern matching; read methods stay open (trusted fallback = noX-Hero-Claims)Files to Modify/Create
Cargo.toml(workspace) — add workspace dephero_proc_sdkcrates/hero_livekit_ui/Cargo.toml— addhero_proc_sdkdepcrates/hero_livekit_ui/src/main.rs— drop hardcoded socket constants, dropserve_unix, usebind_unix_socket(0660 chmod after bind); initHeroLoggerwith sourcehero_livekit_ui; preserveX-Hero-Context/X-Hero-Claims/X-Forwarded-Prefixon the/rpcproxy pathcrates/hero_livekit_server/Cargo.toml— addhero_proc_sdkdepcrates/hero_livekit_server/src/main.rs— initHeroLoggersourcehero_livekit_server; registerAuthorizedOsisLivekitwrapper instead of rawOsisLivekitcrates/hero_livekit_server/src/livekit/server/authz.rs(NEW) — wrapperAuthorizedOsisLivekitimplementingOsisAppRpcHandlerwith context-aware claim enforcementcrates/hero_livekit_server/src/livekit/server/mod.rs—pub mod authz;crates/hero_livekit_examples/examples/health.rs— replace hardcoded path withHERO_SOCKET_DIRresolvercrates/hero_livekit_examples/examples/basic_usage.rs— sameMakefile—SOCKET_DIRhonorsHERO_SOCKET_DIRand exports it to child processesdocs/api.md,docs/architecture.md,docs/configuration.md,docs/ui.md,README.md— update socket path documentation.claude/settings.json— existing curl entries remain valid (default path unchanged); additive allowlist only if neededImplementation Plan
Step 1: Add
hero_proc_sdkworkspace dependencyFiles:
Cargo.tomlActions:
[workspace.dependencies]add:Dependencies: none. Can run in parallel with: Step 2, Step 3, Step 4.
Step 2: Update Makefile to honor HERO_SOCKET_DIR
Files:
MakefileActions:
SOCKET_DIR := $(HOME)/hero/var/sockets/hero_livekitwith a cascade:LOG_DIRalone.run,run-ui,stop,status,restart,restart-ui,stop-uiinherit viaexport.Dependencies: none. Can run in parallel with: Step 1, 3, 4.
Step 3: Fix hardcoded paths in examples
Files:
crates/hero_livekit_examples/examples/health.rs,crates/hero_livekit_examples/examples/basic_usage.rsActions (identical in both):
const SOCKET_PATH/dirs::home_dir()-based resolver.Dependencies: none. Can run in parallel with: all other steps.
Step 4: Update documentation
Files:
docs/api.md,docs/architecture.md,docs/configuration.md,docs/ui.md,README.mdActions:
~/hero/var/sockets/hero_livekit/...with$HERO_SOCKET_DIR/hero_livekit/...in prose/examples; add a one-line note: "defaults to~/hero/var/sockets/hero_livekitwhenHERO_SOCKET_DIRis unset."Dependencies: none. Can run in parallel with: all other steps.
Step 5: Add hero_proc_sdk dep to UI crate
Files:
crates/hero_livekit_ui/Cargo.tomlActions:
[dependencies]:hero_proc_sdk = { workspace = true }tracing-subscriberif present (replaced by HeroLogger).tracing = "0.1"facade.Dependencies: Step 1. Can run in parallel with: Step 6.
Step 6: Add hero_proc_sdk dep to server crate
Files:
crates/hero_livekit_server/Cargo.tomlActions:
[dependencies]:hero_proc_sdk = { workspace = true }.Dependencies: Step 1. Can run in parallel with: Step 5.
Step 7: Rewrite hero_livekit_ui/src/main.rs — sockets + proxy + logging
Files:
crates/hero_livekit_ui/src/main.rsActions:
SERVICE_SOCKET/UI_SOCKETand the functionsservice_socket_path()/ui_socket_path(). Replace with:tracing_subscriber::fmt()....init()inmain()with:AppState:serve_unix(...)(delete it entirely) withbind_unix_socket:rpc_proxy_handlerto preserve the three Hero headers:forward_rpc:tracing_subscriberimport. At bottom ofmain(), callbind_unix_socket(ui_socket_path(), app).await.Dependencies: Step 5. Can run in parallel with: Step 8.
Step 8: Rewrite hero_livekit_server/src/main.rs — HeroLogger + wrapper registration
Files:
crates/hero_livekit_server/src/main.rsActions:
main:OServer::run_cliclosure from registeringOsisLivekitdirectly to creating + wrapping it inAuthorizedOsisLivekitand registering the wrapper viaserver.register_domain(ctx, "livekit", authorized).await?. Preserve data_path resolution and seed handling.Dependencies: Step 6, Step 9. Can run in parallel with: Step 7.
Step 9: Create AuthorizedOsisLivekit wrapper
Files (new):
crates/hero_livekit_server/src/livekit/server/authz.rs; modifycrates/hero_livekit_server/src/livekit/server/mod.rsto addpub mod authz;.Actions:
authz.rs— wrapper delegating toOsisLivekitwith overrides forhandle_service_call_with_contextenforcing claim rules. Rules:*matching the remaining suffix. MissingX-Hero-Claims→ trusted mode → full access.logger.warn("authz", "deny method=... context=... claims=...")and returnRpcError::Operation("PermissionDenied: ...").Dependencies: Step 6. Can run in parallel with: Step 7.
Step 10: Validate compatibility
Files:
.claude/settings.json(no change by default)Actions:
~/hero/var/sockets/hero_livekit/...path. WhenHERO_SOCKET_DIRis unset (default), the resolved path is identical — existing entries keep working.HERO_SOCKET_DIR.Dependencies: none.
Acceptance Criteria
cargo check --workspacepasses withhero_proc_sdkadded.hero_proc_logentries tagged with their source names.HERO_SOCKET_DIR=/tmp/xyz ~/hero/bin/hero_livekit_uicreates/tmp/xyz/hero_livekit/ui.sock(not the default path).stat -c '%a' ~/hero/var/sockets/hero_livekit/ui.sockreturns660.stat -c '%a' ~/hero/var/sockets/hero_livekit/rpc.sockreturns660.serve_unixfunction is gone from the UI; only the newbind_unix_sockethelper remains.X-Hero-Claims: nothing.matchingreturns a JSON-RPC error containingPermissionDenied.X-Hero-Context: 0and noX-Hero-Claimsheader.curl --unix-socket …/rpc.sock -H 'X-Hero-Claims: admin' -X POST http://localhost/rpc -d '{"jsonrpc":"2.0","method":"LiveKitService.start","params":{},"id":1}'succeeds.curl --unix-socket …/rpc.sock -H 'X-Hero-Claims: other.capability' …returnsPermissionDeniedfor the same privileged call./rpcproxy passesX-Hero-Context,X-Hero-Claims,X-Forwarded-Prefixthrough verbatim.Notes
HERO_SOCKET_DIRis the only new env var; both binaries cascade to$HOME/hero/var/socketswhen absent.hero_router, which injectsX-Hero-Claimsbefore the request reachesui.sock.admin,admin.*,hero_livekit.admin,hero_livekit.*. Per-room/resource-level rules are out of scope for this issue.AuthorizedOsisLivekitwrapper is used instead of editing generated code becausebuild.rsregeneratesosis_server_generated.rson every build viahero_rpc_osis::build::OschemaBuilder.HeroLogger::newcan fail ifhero_procisn't running. Propagate the error (fail-fast) since Hero services run underhero_procin all target deployments.hero_rpc_serveralready satisfies most ofhero_socketsforrpc.sock(usesHERO_SOCKET_DIR, 0660 chmod). The bulk of the new work is inhero_livekit_ui.Test Results (Issue #13 implementation)
cargo check --workspacecargo test --workspacecrates/hero_livekit_server/src/livekit/server/authz.rs(rule_matches + required_rules + authorized branches)Breakdown per target:
lk_backend(bin): 0 passed; 0 failed; 0 ignoredhero_livekit_server(lib): 15 passed; 0 failed; 0 ignoredhero_livekit_server(bin): 15 passed; 0 failed; 0 ignoredhero_livekit_ui(bin): 0 passed; 0 failed; 0 ignoredhero_livekit_server: 0 passed; 0 failed; 3 ignoredAll 11
authztests pass in both the lib and bin targets (note: the 10 new tests plus the pre-existingopen_methods_have_no_rulesbaseline run together; every one is green):empty_claim_list_deniesopen_methods_have_no_rulesmatching_claim_authorizesempty_rules_authorizes_any_contextnon_matching_claim_deniesprivileged_methods_have_rulesrule_matches_dotted_literalrule_matches_wildcard_tailwildcard_rule_matches_any_subsegmentrule_matches_literaltrusted_mode_bypasses_rulesNotes
HeroLogger::newrequireshero_procto be reachable; tests that only touchAuthorizedOsisLivekitconstruction (not its logger) should pass unconditionally, but the binaries themselves will fail-fast on startup ifhero_procisn't running — that's expected per the spec.Implementation Summary
All 10 spec steps applied on branch
development_hero_primitives(branched frommain).Changes
Workspace + Cargo manifests
Cargo.toml— addedhero_proc_sdk(git, development branch) to[workspace.dependencies].crates/hero_livekit_server/Cargo.toml— addedhero_proc_sdk = { workspace = true }.crates/hero_livekit_ui/Cargo.toml— addedhero_proc_sdk = { workspace = true }; removedtracing-subscriber.UI binary —
crates/hero_livekit_ui/src/main.rsSERVICE_SOCKET/UI_SOCKETconstants replaced withHERO_SOCKET_DIR-aware resolver (socket_dir()/service_socket_dir()/service_socket_path()/ui_socket_path()), per hero_sockets §7.1.serve_unixaccept loop replaced withbind_unix_sockethelper that chmodsui.sockto0660after bind (hero_sockets §2 + §7.2).tracing_subscriberinit removed;HeroLogger::new("hero_livekit_ui").await?now owns startup logging.AppStatecarriesArc<HeroLogger>.rpc_proxy_handlernow reads and forwardsX-Hero-Context,X-Hero-Claims,X-Forwarded-Prefixto the backendrpc.sock;forward_rpcinjects them into the raw HTTP request.Server binary —
crates/hero_livekit_server/src/main.rsHeroLogger::new("hero_livekit_server").await?initialized at startup; logger passed into the authorization wrapper.OServer::run_cliclosure now hand-constructs eachOsisLivekitviaOsisDomainInit::create, wraps it inAuthorizedOsisLivekit, and registers viaserver.register_domain(ctx, "livekit", authorized)instead of the defaultserver.register::<OsisLivekit>.Authorization wrapper —
crates/hero_livekit_server/src/livekit/server/authz.rs(new)AuthorizedOsisLivekit(Arc<OsisLivekit>, Arc<HeroLogger>)implementingOsisAppRpcHandler.handle_rpc_call/handle_service_call/type_names/oschema_source/openrpc_specto the inner generated type.handle_service_call_with_contextto enforce claim rules on privileged methods:livekitservice.install,configure,start,stop,restart,create_room,delete_room,remove_participant. Rule set:["admin", "admin.*", "hero_livekit.admin", "hero_livekit.*"]. Missing claims header = trusted mode (full access) per hero_context §4.3.handle_rpc_call_with_contextas a pass-through; CRUD methods stay open.HeroLogger(authzsource component) and returnRpcError::Operation("PermissionDenied: ...").crates/hero_livekit_server/src/livekit/server/mod.rsexports the new module viapub mod authz;.Examples — HERO_SOCKET_DIR resolution
crates/hero_livekit_examples/examples/health.rsandexamples/basic_usage.rs—SOCKET_PATHconst +dirs::home_dir()resolver replaced withHERO_SOCKET_DIR/HOMEcascade.Makefile
SOCKET_DIRnow derived fromHERO_SOCKET_DIR ?= $(HOME)/hero/var/sockets.export HERO_SOCKET_DIRpropagates to child processes spawned byrun,run-ui,stop,status,restart, etc.Docs
README.md,docs/api.md,docs/architecture.md,docs/configuration.md,docs/ui.md— replaced literal~/hero/var/sockets/hero_livekit/...with$HERO_SOCKET_DIR/hero_livekit/...and added a top-of-file note explaining the default fallback.Tests — new coverage in
authz.rsrule_matches_literal,rule_matches_wildcard_tail,rule_matches_dotted_literal,wildcard_rule_matches_any_subsegment,privileged_methods_have_rules,open_methods_have_no_rules,trusted_mode_bypasses_rules,empty_rules_authorizes_any_context,matching_claim_authorizes,non_matching_claim_denies,empty_claim_list_denies.Build + Test Results
cargo check --workspace— PASScargo test --workspace— 30 passed, 0 failed, 3 ignored (pre-existing rustdoc examples)Compatibility
HERO_SOCKET_DIRis the only new env var. When unset, resolved paths match the previous defaults exactly, so existing.claude/settings.jsonallowlist entries continue to work without modification.HERO_SOCKET_DIRmust reapprove commands that reference the new path.Out of scope (noted for future work)
HERO_SOCKET_DIR-agnostic parallel block in.claude/settings.json— additive, not blocking; can be added when a developer actively uses a non-defaultHERO_SOCKET_DIR.Pull request opened: #16
This PR implements the changes discussed in this issue.