nix-config/services/matrix-rtc-jwt
2026-11-07 21:55:21 +00:00
..
build.sh matrix-rtc: separate standalone publication from room creation 2026-11-07 21:55:21 +00:00
Dockerfile matrix-rtc: separate standalone publication from room creation 2026-11-07 21:55:21 +00:00
README.md matrix-rtc: separate standalone publication from room creation 2026-11-07 21:55:21 +00:00
standalone-publication.patch matrix-rtc: separate standalone publication from room creation 2026-11-07 21:55:21 +00:00

MatrixRTC standalone publication policy (iscg policy1)

Policy and boundary

This is a narrow fork of Element's lk-jwt-service v0.9.0, upstream commit 0e176b1eca44e67b11484d0bdf56999807bf2b8e (AGPL-3.0-only). It fixes both standalone endpoints, /sfu/get and /get_token:

  • Matrix OpenID verification still runs, including the subject's server name and the claimed user ID where the protocol supplies one.
  • Every successfully authenticated participant may publish audio/video in the requested, room-scoped session, regardless of their homeserver.
  • LIVEKIT_FULL_ACCESS_HOMESERVERS=iscg.dev still gates the server-side CreateRoom call and delayed-leave delegation. It is NOT widened to astr.to or *. No user's JWT gains room creation, list, recording or admin privileges.
  • Existing application-service membership checks, when configured, are unchanged.
  • The SFU MUST retain room.auto_create: false. Otherwise remote users can create arbitrary SFU rooms by joining, regardless of JWT-service policy.
  • A remote user may obtain a scoped token before a local user creates the room; the SFU rejects joining a missing room. This is intentional upstream behaviour.
  • Existing one-hour token TTL, SFU participant limits and container hardening are preserved. No new appservice/admin credential is introduced.

This is not Matrix room-membership authorization. On Heather, Tuwunel 1.9.4 has no MSC4502/MSC4512 integration, and lk-jwt-service is standalone. Someone with valid Matrix OpenID and knowledge of a room ID can request an SFU token even if not a Matrix member, including for an unencrypted call. This limitation existed for subscribing before the patch and now includes publishing. Room IDs are not authorization secrets; E2EE, when enabled by clients, protects media confidentiality, not resource abuse or forged room participation. Do not advertise this deployment as membership-checked or suitable for anonymous guests.

The fuller migration is homeserver-backed MSC4195/MSC4502/MSC4512 integration with scoped application-service membership checks. It requires a homeserver that supports those APIs (or a separately reviewed Tuwunel implementation), client compatibility checks and a backed-up migration. Adding astr.to or * to the full-access list does not solve membership authorization. The newer upstream LIVEKIT_REMOTE_CAN_PUBLISH_HOMESERVERS option applies to federated application-service requests, not to these standalone endpoints.

Source and tests

standalone-publication.patch changes two authorization arguments, adds a startup policy banner, regression tests and updates the two HTTP integration suites' remote publication expectations. Creation, delegation, OpenID and membership-check implementations are not replaced.

The image build fails unless all root-library unit tests and both HTTP/OpenID integration suites (get_token, sfu_get) pass. Regressions cover local users, astr.to, an unrelated server, rejected OpenID, foreign/forged subject, room-scoped JWTs and lack of creation/admin permissions. Existing membership and delayed-event denial tests remain part of the unit suite.

The upstream archive, patch and Rust builder image are SHA256-pinned, and Cargo uses --locked. Alpine build-tool packages are fetched from its package repository, so this is a pinned-source build recipe, NOT a claim of bit-for-bit reproducible output. Preserve the exported release image and its checksum too.

Build (Linux amd64 / Docker)

From this directory:

# Run long work in tmux. No production secrets are needed by the build.
sudo bash build.sh

The resulting image is iscg/lk-jwt-service:0.9.0-policy1-6e21c58c02d6. The image is local-only and Compose uses pull_policy: never. On a replacement host, rebuild it or load the preserved Docker image archive BEFORE starting Arion. Do not replace it with an unreviewed latest tag. Tests and compiler run in build containers; no Rust toolchain is installed on the NixOS host.

Deployment and rollback

Deploy only matrix-rtc-jwt with the evaluated Arion composition, after staging and health/token/media checks. No homeserver, SFU, Coturn or unrelated service restart is required. Compare the Nix source baseline to /run/current-system and inspect dry activation before switching so unrelated edits are not applied.

The deployment receipt records the exact image ID and new Nix generation. Before deployment preserve the old generation, old composition, old image, container start-time baseline and config source. Keep release archives outside cache on Heather and Poppy.

For an emergency rollback, use the saved pre-policy Arion composition:

sudo arion --prebuilt-file /path/from/receipt/old-compose.yaml \
  up -d --no-deps --pull never matrix-rtc-jwt

Then restore the pre-policy Nix generation/source selectively, retaining any subsequent unrelated edits. Do not restart the whole heather.service, wipe volumes, revert the homeserver database or disable TLS verification. Rejoin the call to obtain a fresh JWT; previously issued tokens do not change retroactively.