Migrate hero_livekit to hero_rpc2 (restore main-compat; preserve LiveKitService.* wire contract) #40

Merged
sameh-farouk merged 7 commits from livekit_migration into main 2026-06-04 19:50:58 +00:00
Member

What

Migrates hero_livekit off the retired OServer framework onto hero_rpc2 / HeroOsisServer, restoring buildability and runtime correctness against main-pinned dependencies (post hero_rpc simplification). This supersedes the temporary branch=development dep workaround currently on main (5f34f35).

Why

hero_rpc main dropped hero_rpc_server/hero_rpc_client in favour of hero_rpc2. hero_livekit was the one consumer left un-migrated, so its main no longer built against main-pinned deps. This branch completes the migration so livekit is main-compatible again — no more branch=development pin.

The one non-trivial fix — custom SFU method dispatch

hero_rpc2's adapter registers service methods under the generator's snake_case name (live_kit_service.status) and jsonrpsee matches method names by exact, case-sensitive lookup. Every existing client (generated hero_livekit_sdk, the admin dashboard JS, cross-repo callers) sends the PascalCase wire name LiveKitService.status. The old OServer lowercased inbound methods, so any casing worked; hero_rpc2 does not.

crates/hero_livekit_server/src/main.rs assembles the RpcModule explicitly (mirroring HeroOsisServer::serve()) and registers a LiveKitService.<method> alias for each service-block method, forwarding to the same handle_service_call_with_context entry point under the snake_case dispatch key the generated match arms expect. The LiveKitService.* wire contract is preserved byte-for-byte.

Deps / lockfile (per request)

  • Cargo.toml: all hero_* deps pin branch = "main" (0 branch=development).
  • Cargo.lock: fully refreshed against current main HEADs — hero_rpc2/hero_rpc_osis f6509f9, herolib 9657556a, hero_proc_sdk 0fe2a3d1. cargo metadata --locked consistent; removed hero_rpc_server/hero_rpc_client absent.
  • rust-toolchain.toml: 1.96 (hero_rpc2/herolib_ai require it).

Verification (local, on this box)

Build: cargo build --release --locked --workspace ✓ (clean, only async-trait warnings). Authz suite 11/11 ✓.

Runtime — installed the migrated binaries, restarted via hero_proc, exercised the live socket:

Check Result
LiveKitService.status (PascalCase alias) "running" — dispatches, no -32601
live_kit_service.status (snake native) "running"
LiveKitService.list_rooms → SFU [] (reaches livekit-server)
room.* CRUD (adapter) []
LiveKitService.bogus -32601 (correct negative)
LiveKitService.start → SFU lifecycle SFU livekit-server came up @ 192.168.99.4:7880
GET /openrpc.json 12 methods, PascalCase contract intact
collab huddle end-to-end huddle.start + huddle.join mint a valid JWT (video.room=huddle-10, roomJoin/canPublish, ws_url=ws://192.168.99.4:7880) — no consumer regression

collab mints tokens locally from the hero_proc secret store, independent of livekit's RPC, and the migrated server correctly supervises the SFU those tokens target.

Merge note

main was merged into this branch (not rebased). The interim branch=development workaround is intentionally overridden by the migration's branch=main pins; the publish-CI rustc-1.96 fix from main is carried in.

Risk

Low. Wire contract preserved; build + runtime + cross-consumer (collab) verified. Reviewer holds the merge gate.

## What Migrates `hero_livekit` off the retired `OServer` framework onto **hero_rpc2 / `HeroOsisServer`**, restoring buildability and runtime correctness against `main`-pinned dependencies (post hero_rpc simplification). This **supersedes** the temporary `branch=development` dep workaround currently on `main` (`5f34f35`). ## Why `hero_rpc` `main` dropped `hero_rpc_server`/`hero_rpc_client` in favour of `hero_rpc2`. `hero_livekit` was the one consumer left un-migrated, so its `main` no longer built against `main`-pinned deps. This branch completes the migration so livekit is `main`-compatible again — no more `branch=development` pin. ## The one non-trivial fix — custom SFU method dispatch hero_rpc2's adapter registers service methods under the generator's **snake_case** name (`live_kit_service.status`) and jsonrpsee matches method names by exact, case-sensitive lookup. Every existing client (generated `hero_livekit_sdk`, the admin dashboard JS, cross-repo callers) sends the **PascalCase** wire name `LiveKitService.status`. The old `OServer` lowercased inbound methods, so any casing worked; hero_rpc2 does not. `crates/hero_livekit_server/src/main.rs` assembles the `RpcModule` explicitly (mirroring `HeroOsisServer::serve()`) and registers a `LiveKitService.<method>` alias for each service-block method, forwarding to the same `handle_service_call_with_context` entry point under the snake_case dispatch key the generated match arms expect. The `LiveKitService.*` wire contract is preserved byte-for-byte. ## Deps / lockfile (per request) - `Cargo.toml`: **all** hero_* deps pin `branch = "main"` (0 `branch=development`). - `Cargo.lock`: **fully refreshed** against current `main` HEADs — hero_rpc2/hero_rpc_osis `f6509f9`, herolib `9657556a`, hero_proc_sdk `0fe2a3d1`. `cargo metadata --locked` consistent; removed `hero_rpc_server`/`hero_rpc_client` absent. - `rust-toolchain.toml`: `1.96` (hero_rpc2/herolib_ai require it). ## Verification (local, on this box) Build: `cargo build --release --locked --workspace` ✓ (clean, only async-trait warnings). Authz suite 11/11 ✓. Runtime — installed the migrated binaries, restarted via `hero_proc`, exercised the live socket: | Check | Result | |---|---| | `LiveKitService.status` (PascalCase alias) | `"running"` — dispatches, no `-32601` | | `live_kit_service.status` (snake native) | `"running"` | | `LiveKitService.list_rooms` → SFU | `[]` (reaches livekit-server) | | `room.*` CRUD (adapter) | `[]` | | `LiveKitService.bogus` | `-32601` (correct negative) | | `LiveKitService.start` → SFU lifecycle | SFU `livekit-server` came up @ `192.168.99.4:7880` | | `GET /openrpc.json` | 12 methods, PascalCase contract intact | | **collab huddle end-to-end** | `huddle.start` + `huddle.join` mint a **valid JWT** (`video.room=huddle-10`, roomJoin/canPublish, `ws_url=ws://192.168.99.4:7880`) — **no consumer regression** | collab mints tokens locally from the hero_proc secret store, independent of livekit's RPC, and the migrated server correctly supervises the SFU those tokens target. ## Merge note `main` was merged into this branch (not rebased). The interim `branch=development` workaround is intentionally overridden by the migration's `branch=main` pins; the publish-CI rustc-1.96 fix from `main` is carried in. ## Risk Low. Wire contract preserved; build + runtime + cross-consumer (collab) verified. Reviewer holds the merge gate.
feat(livekit): port server to the main OSIS framework; align deps to hero_lib main
Some checks failed
Build & Test / check (push) Failing after 36s
e0b1de6eb6
Ecosystem main alignment + migration of hero_livekit_server off the removed
hero_rpc_server framework onto the current hero_rpc_osis OSIS bootstrap.

Deps:
- All git deps dev -> main; drop dead hero_rpc_client + removed hero_rpc_server.
- Add herolib_openrpc (SDK client macro), herolib_oschema (OsisObject derive),
  hero_lifecycle (base service helpers); pin rust-toolchain to 1.96.

Server migration (old flat-generated + sync framework -> new barrel + async):
- build.rs: drop obsolete post-processing; pin the domain module wiring
  (the generator scaffolds a mod.rs that never declares `mod generated`) and
  inject the orphaned `generated/osis_impl.rs` so DBTyped<T>: OsisObject holds.
- rpc.rs: hand-written LiveKitServiceHandler (keeps child-process + runtime
  state) now implements the async LiveKitServiceTrait; twirp_call awaited
  directly (block_on shim removed); create_room uses the new room_new(input)
  CRUD; CRUD calls awaited.
- authz.rs: AuthorizedOsisLivekit implements the now-async OsisAppRpcHandler +
  OsisDomainInit::create; method-name gate normalized for both LiveKitService.*
  and live_kit_service.* dispatch forms.
- main.rs: bootstrap via HeroOsisServer::with_domain::<AuthorizedOsisLivekit>.
- base helpers: herolib_core::base::* -> hero_lifecycle::base::* across bins.

Workspace builds green on 1.96 (hero_lib main only, zero dev sources); clippy
clean; authz unit tests pass.
fix(livekit): serve /openrpc.json + rpc.discover via with_openrpc_spec
Some checks failed
Build & Test / check (push) Failing after 37s
06228dfac7
HeroOsisServer only wires GET /openrpc.json and the rpc.discover method when
the caller embeds the spec. Embed the build-generated crate-root openrpc.json
and pass it via .with_openrpc_spec() — fixes the lab smoke test
'GET /openrpc.json: HTTP 404'.
fix(livekit): use canonical crate_name/bin_name in admin dependency
Some checks failed
Build & Test / check (push) Failing after 19s
ce7d5340c1
The hero_lifecycle service.toml schema names dependency keys `crate_name` and
`bin_name` (no serde renames). The admin's [[dependencies]] used `crate`/`bin`,
which were silently dropped — so lab resolved the dep to the repo basename
`hero_livekit` (a non-existent binary) instead of `hero_livekit_server`, and
failed to start the admin. Rename to the schema keys.
The OServer→HeroOsisServer migration left the service-block lifecycle
methods (LiveKitService.install/configure/start/.../issue_token)
undispatchable: every call returned -32601 while rpc.discover and
/openrpc.json still advertised them.

Root cause: hero_rpc2's rpc2_adapter registers service methods with
jsonrpsee under the *lowercased* name taken from
OsisAppRpcHandler::openrpc_spec() (the build-generated
docs/livekit/openrpc.json, whose service prefix is
to_snake_case("LiveKitService") = "live_kit_service"). So the server
registers `live_kit_service.install`. jsonrpsee matches method names by
exact, case-sensitive HashMap lookup, but every client — the generated
hero_livekit_sdk, the admin dashboard.js, and cross-repo callers like
hero_collab — sends the PascalCase wire name `LiveKitService.install`.
No match → -32601. The old OServer lowercased the *inbound* method
before dynamic dispatch, so any casing worked; hero_rpc2 hands the raw
body straight to RpcModule::raw_json_request with no rewrite, and
HeroOsisServer exposes no hook to register a method under the exact name
a client sends.

Fix: hero_livekit_server now assembles the RpcModule itself
(rpc2_adapter::module_for for CRUD + the generator-native
live_kit_service.* methods) and additionally registers a
`LiveKitService.<method>` alias for each service-block method,
forwarding to the same handle_service_call_with_context entry point
under the snake_case dispatch key the generated OsisLivekit::handle_rpc
arms expect. Everything else (banner, socket prep, lifted headers,
service info, rpc.discover/openrpc.json, graceful shutdown) mirrors
HeroOsisServer::serve() exactly. Claim-based authz is unchanged — the
alias forwards through AuthorizedOsisLivekit, so privileged methods stay
gated.

No generated files or upstream framework code were edited; the
LiveKitService.* wire contract is preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The migration toml already pins every hero_* dep to branch=main. Refresh
the lockfile so it resolves to current main HEADs across the board:
hero_proc_sdk b60e0c58 -> 0fe2a3d1 (current hero_proc main). hero_rpc2,
hero_rpc_osis (f6509f9) and herolib (9657556a) already at main HEAD.

Build (release, --locked) green; runtime-verified the migrated server
dispatches the LiveKitService.* wire contract and supervises the SFU.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge origin/main into livekit_migration
Some checks failed
Build & Test / check (push) Failing after 19s
Build & Test / check (pull_request) Failing after 20s
3cef715213
Brings the interim main repair commits into the migration branch so the
PR merges cleanly back to main:
  - 4199c6e/89ad330  publish CI: rustc 1.96 via RUSTUP_TOOLCHAIN + install
  - 5f34f35/776b8ec  interim 'branch=development' dep workaround + --locked

The dep workaround (branch=development) is SUPERSEDED by this branch's
proper hero_rpc2 main-pins, so Cargo.toml/Cargo.lock keep the migration's
branch=main resolution (verified: 0 branch=development left; lock pins at
main HEAD; cargo --locked consistent; old hero_rpc_server/client absent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci(livekit): make build.yml green for the hero_rpc2 generated/ layout
All checks were successful
Build & Test / check (push) Successful in 2m51s
Build & Test / check (pull_request) Successful in 5m59s
9df12ae6b6
The migration adopts the gitignored `src/livekit/generated/` codegen layout
(hero_rpc#96). Three CI gates broke against it; fix each at the source:

- build.yml: run Check BEFORE Format check. `cargo fmt --check` cannot
  resolve `mod generated;` in a clean checkout (the dir is build.rs output);
  a build/codegen pass must materialize + rustfmt it first.
- build.rs: add `#![allow(async_fn_in_trait)]` to the pinned mod.rs so
  `make clippy` (-D warnings) tolerates the generator's async-fn service
  traits in generated/rpc.rs (generator-owned, can't hand-edit).
- examples: the migrated SDK surfaces `herolib_openrpc` errors, not
  `hero_rpc_openrpc`; return `Box<dyn Error>` from the example mains and drop
  the now-unused hero_rpc_openrpc dep (lock edge removed, no version bumps).

Verified clean-room (rm generated/ then): check + fmt-check + clippy
(-D warnings) + test all exit 0; authz suite 11/11.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
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_livekit!40
No description provided.