@volter/twin-livekit
v0.1.36
Published
Local LiveKit control-plane twin built on @volter/world-core.
Readme
@volter/twin-livekit
A local LiveKit control-plane twin for offline room and egress tests. It
models the Twirp JSON APIs used by livekit-server-sdk for room lifecycle,
participant inspection, metadata updates, and room-composite egress.
world-livekit serve [--port N] [--root DIR] [--read-only]
world-livekit conformance [--root DIR]Coverage
The capability manifest (src/livekit-capabilities.ts) is an honest partial
denominator for LiveKit server APIs: RoomService, Egress, Ingress, SIP, agent
dispatch, webhooks, tokens, recording outputs, auth/error behavior, and media
plane behavior are enumerated.
Modeled:
- RoomService:
CreateRoom,ListRooms,DeleteRoom,UpdateRoomMetadata,ListParticipants,GetParticipant,RemoveParticipant,UpdateParticipant(including atomic permission grant replacement),MutePublishedTrack,UpdateSubscriptions,SendData,ForwardParticipant(mirror into a destination room; mirror cannot publish) andMoveParticipant(relocate; left source, present in destination). - Participants/tracks: deterministic local participant rows with track
source and muted state, permission grants, and per-participant subscription
sets, including the
SCREEN_SHAREchecks Runhuman uses. Participant rows are seeded through the local-onlylivekit.Twintest hook; the real WebRTC join/media path is not claimed as vendor API coverage. - Egress: all five start verbs —
StartRoomCompositeEgress,StartWebEgress,StartParticipantEgress,StartTrackCompositeEgress,StartTrackEgress(each tagged with the correct protobufrequestoneof case) — plusListEgress,StopEgress,UpdateLayout,UpdateStream(add/remove stream URLs), segment outputs, image (thumbnail) outputs, the per-egresswebhookUrlconfig field, and terminal failure-state transitions (FAILED/ABORTED/LIMIT_REACHED). Image/webhook config is persisted on the request and round-trips viaListEgress; this twin records the control-plane state rather than running egress workers that render bytes. - Ingress:
CreateIngress,UpdateIngress,ListIngress,DeleteIngress; RTMP/WHIP/URL input types with stream-key/URL minting and the RTMP-transcoding vs WHIP-bypass defaults. - SIP (
SIPService):CreateSIPInboundTrunk/CreateSIPOutboundTrunk+ListSIPInboundTrunk/ListSIPOutboundTrunk(filter by id/number) +DeleteSIPTrunk;CreateSIPDispatchRule(direct + individualruleoneof) +ListSIPDispatchRule(filter by id/trunk, wildcard rules match) +DeleteSIPDispatchRule;CreateSIPParticipant(dials over a known outbound trunk and adds akind=SIProom participant) +TransferSIPParticipant(records the transfer destination on participant attributes). Trunk/rule update verbs are still TODO. This twin models SIP control, not signalling/RTP. - Agent dispatch (
AgentDispatchService):CreateDispatch,ListDispatch(filter by room + dispatch id), andDeleteDispatch. This twin models the dispatch records, not an agent worker runtime. - Access tokens:
mintAccessToken/verifyAccessTokenproduce and check HS256 grant JWTs that are byte-compatible with the SDK'sAccessTokenandTokenVerifier(video + SIP grants, exp/nbf, forgery rejection). - Webhooks:
buildWebhookBody/signWebhookHeader/verifyWebhookproduce and verify signed webhook deliveries (HMAC auth header + bodysha256) that round-trip with the SDK'sWebhookReceiver, covering room, participant, egress, and ingress events. - Auth & errors: opt-in access-token enforcement (
apiKeyson the server / handler) yields vendor-faithfulunauthenticated(missing/invalid/expired token) andpermission_denied(missing grant) Twirp errors, plus anresource_exhaustedegress capacity quota. - SDK fidelity: tests drive the unmodified
RoomServiceClient,EgressClient,IngressClient,SipClient,AgentDispatchClient,AccessToken/TokenVerifier, andWebhookReceiveragainst the local twin. - Connector: injected-client pull folds rooms/participants/egresses/ingresses
into local state (
pullLiveKitSnapshot), plus SIP trunks/dispatch rules (pullLiveKitSIP) and per-room agent dispatches (pullLiveKitAgentDispatch), all idempotent; push confirms pending room metadata and egress stop actions.
Planned (todo):
- Outbound webhook delivery — POST the signed event bodies the twin already
builds to the configured
webhookUrl, with the documented retry schedule. - Still TODO (not faked): SIP trunk/dispatch-rule update verbs
(
UpdateSIPInboundTrunk/UpdateSIPOutboundTrunk/UpdateSIPDispatchRule, replace + field-merge). Unmodeled SIP/agent ops fail with a vendor-faithfulnot_foundTwirp error rather than being faked.
No UI mirror
LiveKit is a vendor whose product is the API: the twin models the server-side SFU control plane (RoomService, Egress, Ingress, SIP, agent dispatch, tokens, webhooks), which applications drive from code. LiveKit Cloud's console is incidental project/key tooling, not where the work happens. Per ../../../docs/contributing/adding-a-twin.md ("Does this vendor get a mirror?") and ../../../docs/contributing/architecture.md C1b, this pack ships no React mirror and no UI capabilities — omit rather than fabricate a dashboard. Coverage is API + connector + tokens.
The store door
LiveKit's server API is Twirp: every operation, ListRooms included, is a POST to
/twirp/livekit.<Service>/<Method>. The vendor surface has no GET, so twin state had no
ordinary read. GET /twin/store/{rooms,participants,egress,ingress} is that read — the pack's
own named deterministic projections over stored state, served by the same fetch adapter the
Twirp paths come out of (the mailgun precedent). They are deliberately out of the capability
manifest: counting scaffolding the vendor does not have would pad the denominator. GET /twin
names them under stores.
Architecture
State lives in the @volter/world-core event/action log. The serve path makes no real
LiveKit calls. Connector functions accept injected executors for real LiveKit
I/O and are not used by the local handler.
