jrtc 3.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. jrtc-3.1.0/LICENSE +21 -0
  2. jrtc-3.1.0/MANIFEST.in +2 -0
  3. jrtc-3.1.0/PKG-INFO +535 -0
  4. jrtc-3.1.0/README.md +499 -0
  5. jrtc-3.1.0/pyproject.toml +99 -0
  6. jrtc-3.1.0/setup.cfg +4 -0
  7. jrtc-3.1.0/src/jrtc/__init__.py +40 -0
  8. jrtc-3.1.0/src/jrtc/auth.py +60 -0
  9. jrtc-3.1.0/src/jrtc/conf/__init__.py +4 -0
  10. jrtc-3.1.0/src/jrtc/conf/_config.py +173 -0
  11. jrtc-3.1.0/src/jrtc/conf/_janus.py +70 -0
  12. jrtc-3.1.0/src/jrtc/conf/settings/__init__.py +45 -0
  13. jrtc-3.1.0/src/jrtc/conf/settings/global_settings.py +81 -0
  14. jrtc-3.1.0/src/jrtc/core/__init__.py +29 -0
  15. jrtc-3.1.0/src/jrtc/core/exceptions.py +73 -0
  16. jrtc-3.1.0/src/jrtc/core/logging/__init__.py +19 -0
  17. jrtc-3.1.0/src/jrtc/core/logging/_json.py +96 -0
  18. jrtc-3.1.0/src/jrtc/core/logging/formatting.py +58 -0
  19. jrtc-3.1.0/src/jrtc/core/logging/utils.py +165 -0
  20. jrtc-3.1.0/src/jrtc/core/utils.py +29 -0
  21. jrtc-3.1.0/src/jrtc/lib/__init__.py +5 -0
  22. jrtc-3.1.0/src/jrtc/lib/manager.py +149 -0
  23. jrtc-3.1.0/src/jrtc/lib/plugins/__init__.py +0 -0
  24. jrtc-3.1.0/src/jrtc/lib/plugins/base.py +446 -0
  25. jrtc-3.1.0/src/jrtc/lib/registry.py +149 -0
  26. jrtc-3.1.0/src/jrtc/lib/utils.py +20 -0
  27. jrtc-3.1.0/src/jrtc/manager.py +297 -0
  28. jrtc-3.1.0/src/jrtc/messaging/__init__.py +57 -0
  29. jrtc-3.1.0/src/jrtc/messaging/constants.py +53 -0
  30. jrtc-3.1.0/src/jrtc/messaging/dispatcher.py +241 -0
  31. jrtc-3.1.0/src/jrtc/messaging/engines/__init__.py +9 -0
  32. jrtc-3.1.0/src/jrtc/messaging/engines/kafka.py +636 -0
  33. jrtc-3.1.0/src/jrtc/messaging/factory.py +117 -0
  34. jrtc-3.1.0/src/jrtc/messaging/listeners.py +122 -0
  35. jrtc-3.1.0/src/jrtc/messaging/metrics.py +115 -0
  36. jrtc-3.1.0/src/jrtc/messaging/publisher.py +544 -0
  37. jrtc-3.1.0/src/jrtc/models/__init__.py +4 -0
  38. jrtc-3.1.0/src/jrtc/models/base.py +27 -0
  39. jrtc-3.1.0/src/jrtc/models/common.py +71 -0
  40. jrtc-3.1.0/src/jrtc/models/request.py +156 -0
  41. jrtc-3.1.0/src/jrtc/models/response.py +197 -0
  42. jrtc-3.1.0/src/jrtc/py.typed +1 -0
  43. jrtc-3.1.0/src/jrtc/session/__init__.py +23 -0
  44. jrtc-3.1.0/src/jrtc/session/base.py +387 -0
  45. jrtc-3.1.0/src/jrtc/session/websocket.py +281 -0
  46. jrtc-3.1.0/src/jrtc/transport/__init__.py +22 -0
  47. jrtc-3.1.0/src/jrtc/transport/base.py +47 -0
  48. jrtc-3.1.0/src/jrtc/transport/http.py +490 -0
  49. jrtc-3.1.0/src/jrtc/transport/websocket.py +457 -0
  50. jrtc-3.1.0/src/jrtc.egg-info/PKG-INFO +535 -0
  51. jrtc-3.1.0/src/jrtc.egg-info/SOURCES.txt +57 -0
  52. jrtc-3.1.0/src/jrtc.egg-info/dependency_links.txt +1 -0
  53. jrtc-3.1.0/src/jrtc.egg-info/requires.txt +12 -0
  54. jrtc-3.1.0/src/jrtc.egg-info/top_level.txt +1 -0
  55. jrtc-3.1.0/tests/test_dependency_hygiene.py +61 -0
  56. jrtc-3.1.0/tests/test_kafka_engine.py +418 -0
  57. jrtc-3.1.0/tests/test_messaging.py +509 -0
  58. jrtc-3.1.0/tests/test_plugin_local_events.py +161 -0
  59. jrtc-3.1.0/tests/test_response_parser.py +34 -0
jrtc-3.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Leydotpy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
jrtc-3.1.0/MANIFEST.in ADDED
@@ -0,0 +1,2 @@
1
+ prune packages
2
+ exclude tests/test_package_separation.py
jrtc-3.1.0/PKG-INFO ADDED
@@ -0,0 +1,535 @@
1
+ Metadata-Version: 2.4
2
+ Name: jrtc
3
+ Version: 3.1.0
4
+ Summary: Plugin-agnostic async Python core for the Janus WebRTC Gateway
5
+ Author-email: Leydotpy <leydotpy.dev@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://janus.conf.meetecho.com/
8
+ Project-URL: Documentation, https://janus.conf.meetecho.com/docs/
9
+ Project-URL: Repository, https://github.com/Leydotpy/Janus-API
10
+ Project-URL: Issues, https://github.com/Leydotpy/Janus-API/issues
11
+ Keywords: janus,webrtc,asyncio,websocket,media-server
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Communications
20
+ Classifier: Topic :: Internet
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.12
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: broka==0.0.2
26
+ Requires-Dist: dispio==0.0.2
27
+ Requires-Dist: logvista>=0.1.0
28
+ Requires-Dist: pydantic<3,>=2.10
29
+ Requires-Dist: websockets<17,>=14
30
+ Provides-Extra: http
31
+ Requires-Dist: httpx<1,>=0.27; extra == "http"
32
+ Requires-Dist: idna<4,>=3.15; extra == "http"
33
+ Provides-Extra: kafka
34
+ Requires-Dist: aiokafka<1,>=0.14; extra == "kafka"
35
+ Dynamic: license-file
36
+
37
+ # Janus Core
38
+
39
+ Production-oriented, async Python foundations for the
40
+ [Janus WebRTC Gateway](https://janus.conf.meetecho.com/): sessions, transports,
41
+ authentication, protocol envelopes, plugin-handle lifecycle, event routing, and
42
+ session-pool ownership. The separately installable operations server contains
43
+ the FastAPI, monitoring, administration, and persistence surfaces.
44
+
45
+ Janus Core intentionally contains **no named Janus plugin implementation**.
46
+ EchoTest, VideoCall, SIP, NoSIP, AudioBridge, VideoRoom, TextRoom, and
47
+ Record&Play are independent projects in `plugins/`; Streaming lives entirely
48
+ in `plugins/janus-streaming-plugin`. Applications can install only what
49
+ they use or implement their own client on the public `Plugin` base.
50
+
51
+ > The product is named **Janus Core**. The exact `janus-core` distribution and
52
+ > `janus_core` import namespace are already occupied on PyPI by an unrelated
53
+ > project, so this project uses the publishable distribution name
54
+ > `jrtc` and retains the stable `jrtc` Python namespace.
55
+
56
+ ## Requirements and installation
57
+
58
+ - Python 3.12+
59
+ - Janus Gateway with at least one enabled API transport
60
+
61
+ ```bash
62
+ pip install jrtc
63
+
64
+ # Plain HTTP/HTTPS transport
65
+ pip install "jrtc[http]"
66
+
67
+ # Explicit Kafka client support for the core broker adapter
68
+ pip install "jrtc[kafka]"
69
+
70
+ # Independent FastAPI monitoring and administration service
71
+ pip install "japi[ops]"
72
+ ```
73
+
74
+ The core runtime uses Pydantic, WebSockets, LogVista, Dispio, and Broka. Broka
75
+ 0.0.2 currently declares its Redis, RabbitMQ, and Kafka clients as required
76
+ dependencies, even when an in-process engine is selected. HTTP transport is the
77
+ core transport extra; the Kafka extra explicitly declares the client used by
78
+ the hardened adapter instead of relying on Broka's transitive metadata.
79
+ `japi` directly depends on core but core never imports FastAPI,
80
+ Starlette, Uvicorn, asyncpg, or the server package.
81
+
82
+ ### Migrating from the bundled operations server
83
+
84
+ The 3.1 package boundary is intentionally explicit:
85
+
86
+ - Replace `jrtc.create_asgi_app` and `jrtc.api` imports with
87
+ `japi.create_asgi_app` and `japi.api`.
88
+ - Replace `jrtc.contrib.admin.db.migrate` with
89
+ `japi.contrib.admin.db.migrate`.
90
+ - Import `JanusSessionManager` from `jrtc` (or `jrtc.session`). The
91
+ former mixed `jrtc.servers` namespace is gone.
92
+ - Remove callers of the former `/manager` API. The operations service neither
93
+ owns nor exposes application session pools.
94
+ - Configure EventHandler delivery with `JANUS_EVENT_BROKER_*` settings. The
95
+ legacy Kafka topic variable is accepted only as a destination fallback.
96
+
97
+ EventHandler records are now delivered using Broka's envelope wire format,
98
+ not the previous raw-JSON Kafka value. Native Kafka consumers must decode the
99
+ Broka envelope and deduplicate on its stable `janus-event-id` header; they must
100
+ not use the transport-generated envelope ID as the retry key. Migrate consumers
101
+ before routing an existing production topic through the new server.
102
+
103
+ The old operations flags are not read by the new distribution. Rename them as
104
+ part of the same deployment:
105
+
106
+ | Previous variable | Server variable |
107
+ |---|---|
108
+ | `JANUS_MOUNT_REST_API` | `JANUS_SERVER_MOUNT_REST_API` |
109
+ | `JANUS_ENABLE_ADMIN` | `JANUS_SERVER_ENABLE_ADMIN` |
110
+ | `JANUS_ENABLE_EVENTS` | `JANUS_SERVER_ENABLE_EVENTS` |
111
+ | `JANUS_MOUNT_LOGGING_APP` | `JANUS_SERVER_MOUNT_LOGGING_APP` |
112
+ | `JANUS_ALLOWED_ORIGINS` | `JANUS_SERVER_ALLOWED_ORIGINS` |
113
+ | `jrtc_ALLOW_CREDENTIALS` | `JANUS_SERVER_API_ALLOW_CREDENTIALS` |
114
+
115
+ ## Client quick start
116
+
117
+ Install only the named plugin an application needs:
118
+
119
+ ```bash
120
+ pip install janus-echotest-plugin
121
+ ```
122
+
123
+ ```python
124
+ import asyncio
125
+
126
+ from jrtc import JanusSession
127
+ from janus_echotest_plugin import EchoTestPlugin
128
+
129
+
130
+ async def main() -> None:
131
+ async with JanusSession(url="ws://127.0.0.1:8188/janus") as session:
132
+ async with EchoTestPlugin(session=session) as echo:
133
+ reply = await echo.configure(audio=True, video=False)
134
+ print(reply.data)
135
+
136
+
137
+ asyncio.run(main())
138
+ ```
139
+
140
+ Use the same session API with Janus REST; long polling and REST path addressing
141
+ are managed by the HTTP transport:
142
+
143
+ ```python
144
+ async with JanusSession(url="http://127.0.0.1:8088/janus") as session:
145
+ ...
146
+ ```
147
+
148
+ WebSocket disconnects invalidate every session and handle bound to that socket.
149
+ The manager creates fresh sessions rather than silently reusing stale Janus IDs.
150
+ Requests have bounded transaction tables and explicit timeouts; cancellation
151
+ always releases the pending transaction. Shutdown detaches handles with bounded
152
+ concurrency, reserves time for the Janus session destroy, and completes local
153
+ cleanup even if the calling task is cancelled.
154
+
155
+ WebSocket and HTTP are the built-in transports. RabbitMQ, MQTT, nanomsg, Unix
156
+ sockets, or an application-specific Janus transport can implement the small
157
+ `JanusTransport` protocol and be injected without core interpreting its URL:
158
+
159
+ ```python
160
+ from jrtc import JanusSession
161
+
162
+ session = JanusSession(
163
+ transport=my_transport,
164
+ url="unix:///run/janus.sock", # owned by the injected transport
165
+ )
166
+ await session.create()
167
+ ```
168
+
169
+ Pass either `transport` or `transport_factory`, never both. A factory-created
170
+ transport is owned and closed by the session; a directly injected transport is
171
+ treated as shared and remains owned by the host application.
172
+
173
+ ## Brokered WebRTC events
174
+
175
+ Inbound responses are coordinated once by a frozen Dispio dispatcher. ACK,
176
+ error, and transaction responses stay local; asynchronous Janus responses are
177
+ admitted directly from either transport to a bounded, ordered Broka publisher.
178
+ They do not pass through plugin callbacks on their way to third-party
179
+ applications, and ReactiveX is not part of the runtime.
180
+
181
+ Every event uses a logical type such as `janus.event` or `janus.media`, mapped
182
+ to one portable physical destination, `janus.events`. JSEP remains embedded in
183
+ the complete `janus.event` payload—there is no second SDP event.
184
+
185
+ The application that owns the Janus session also owns and injects its publisher.
186
+ The operations server deliberately creates neither one, which prevents duplicate
187
+ Janus sessions when both packages run in the same process:
188
+
189
+ ```python
190
+ from jrtc import JanusSession
191
+ from jrtc.messaging import JanusEventPublisher, create_broker
192
+
193
+ broker = create_broker(
194
+ engine="redis",
195
+ engine_options={
196
+ "mode": "streams",
197
+ "url": "redis://localhost:6379/0",
198
+ },
199
+ )
200
+
201
+ async with JanusEventPublisher(broker) as publisher:
202
+ async with JanusSession(event_publisher=publisher) as session:
203
+ ...
204
+ ```
205
+
206
+ Third-party applications subscribe to the exact physical destination and route
207
+ on `delivery.envelope.type`:
208
+
209
+ ```python
210
+ from jrtc.messaging import DEFAULT_PHYSICAL_ROUTE, create_broker
211
+
212
+ subscriber = create_broker(
213
+ engine="redis",
214
+ engine_options={
215
+ "mode": "streams",
216
+ "url": "redis://localhost:6379/0",
217
+ "group": "analytics-janus-v1",
218
+ "consumer_name": "analytics-01",
219
+ },
220
+ )
221
+
222
+
223
+ async def receive(delivery) -> None:
224
+ logical_type = delivery.envelope.type
225
+ response = delivery.message
226
+ jsep = response.get("jsep")
227
+ await application_events.handle(logical_type, response, jsep=jsep)
228
+
229
+
230
+ async with subscriber:
231
+ subscription = await subscriber.subscribe(DEFAULT_PHYSICAL_ROUTE, receive)
232
+ try:
233
+ await stop_event.wait()
234
+ finally:
235
+ await subscription.close()
236
+ ```
237
+
238
+ Redis Pub/Sub, Redis Streams, RabbitMQ, Kafka, native wire consumers,
239
+ acknowledgement semantics, stable consumer identities, and current upstream
240
+ adapter limits are covered in the
241
+ [broker event guide](https://github.com/Leydotpy/Janus-API/blob/main/docs/broker-events.md).
242
+
243
+ ## Authentication
244
+
245
+ Janus API tokens and API secrets belong to the outer envelope of every request.
246
+ Configure them once per session; representations and settings inspection redact
247
+ their values.
248
+
249
+ ```python
250
+ from jrtc import JanusCredentials, JanusSession
251
+
252
+ credentials = JanusCredentials(token="client-token", api_secret="shared-secret")
253
+
254
+ async with JanusSession(credentials=credentials) as session:
255
+ ...
256
+ ```
257
+
258
+ A callable returning `JanusCredentials` may be supplied for credential rotation.
259
+ The default session manager reads `JANUS_TOKEN` and `jrtc_SECRET`.
260
+
261
+ ## Plugin projects
262
+
263
+ The sibling `plugins/` workspace contains independent distributions:
264
+
265
+ | Janus plugin | Distribution | Import package | Entry-point name |
266
+ |---|---|---|---|
267
+ | EchoTest | `janus-echotest-plugin` | `janus_echotest_plugin` | `echotest` |
268
+ | VideoCall | `janus-videocall-plugin` | `janus_videocall_plugin` | `videocall` |
269
+ | SIP | `janus-sip-plugin` | `janus_sip_plugin` | `sip` |
270
+ | NoSIP | `janus-nosip-plugin` | `janus_nosip_plugin` | `nosip` |
271
+ | AudioBridge | `janus-audiobridge-plugin` | `janus_audiobridge_plugin` | `audiobridge` |
272
+ | VideoRoom | `janus-videoroom-plugin` | `janus_videoroom_plugin` | `videoroom` |
273
+ | TextRoom | `janus-textroom-plugin` | `janus_textroom_plugin` | `textroom` |
274
+ | Record&Play | `janus-recordplay-plugin` | `janus_recordplay_plugin` | `recordplay` |
275
+
276
+ Streaming remains in the `plugins/janus-streaming-plugin`
277
+ workspace as distribution `janus-api-streaming`, import package
278
+ `janus_streaming`, and entry-point name `streaming`.
279
+
280
+ Installed plugins are discovered lazily through the `jrtc.plugins` entry
281
+ point group. Importing Janus Core never scans or executes arbitrary local files
282
+ and never imports unrelated named plugins.
283
+
284
+ ```python
285
+ from jrtc.lib import Plugin
286
+
287
+ # Resolves only the installed `echotest` entry point.
288
+ echo = Plugin(identifier="echotest", session=session)
289
+ await echo.attach()
290
+ ```
291
+
292
+ Direct construction of the concrete class is preferred because it gives type
293
+ checkers the plugin-specific methods and result types.
294
+
295
+ ## Implementing a custom plugin
296
+
297
+ Only the generic base and shared protocol primitives are required:
298
+
299
+ ```python
300
+ from typing import Literal
301
+
302
+ from pydantic import BaseModel
303
+
304
+ from jrtc.lib import Plugin
305
+
306
+
307
+ class StatusRequest(BaseModel):
308
+ request: Literal["status"] = "status"
309
+
310
+
311
+ class MyPlugin(Plugin):
312
+ identifier = "my-plugin"
313
+ name = "janus.plugin.my-plugin"
314
+
315
+ async def status(self):
316
+ return await self.send(StatusRequest())
317
+ ```
318
+
319
+ To support lazy discovery from a separate distribution:
320
+
321
+ ```toml
322
+ [project.entry-points."jrtc.plugins"]
323
+ my-plugin = "my_package.plugin:MyPlugin"
324
+ ```
325
+
326
+ Plugin response bodies are opaque to core. A plugin package should use strict
327
+ outbound models, forward-compatible inbound models, typed plugin errors, and
328
+ golden protocol tests derived from its Janus documentation.
329
+
330
+ ## Session ownership and the separate operations server
331
+
332
+ `JanusSessionManager` remains part of `jrtc` and owns a bounded,
333
+ process-local session pool. Each application owns its Janus connections and
334
+ passes the selected session to its own plugins. The operations server never
335
+ creates, installs, or tears down a session or manager.
336
+
337
+ ```python
338
+ from jrtc import JanusSessionManager
339
+
340
+ async with JanusSessionManager(pool_size=2) as manager:
341
+ session = manager.get_session(key="tenant-42")
342
+ if session is None:
343
+ raise RuntimeError("Janus is unavailable")
344
+ ```
345
+
346
+ Install `japi` independently for monitoring and administration. Its
347
+ ASGI lifespan owns only server resources such as the Admin monitor, optional
348
+ Timescale storage, EventHandler broker sink, and log tailer:
349
+
350
+ ```python
351
+ from japi import create_asgi_app
352
+
353
+ app = create_asgi_app(mount_rest_api=True)
354
+ ```
355
+
356
+ ```bash
357
+ JANUS_SERVER_MOUNT_REST_API=true \
358
+ uvicorn myapp:app --host 0.0.0.0 --port 8000
359
+ ```
360
+
361
+ When enabled, the `/janus` mount can expose:
362
+
363
+ - `/admin/` — authenticated Admin/Monitor JSON, Prometheus, and WebSocket APIs
364
+ - `/events/janus-events` — bounded, Basic-authenticated EventHandler ingestion to Broka
365
+ - `/logs/` and `/logs/ws/logs` — bounded API-key-authenticated structured log access
366
+
367
+ There is intentionally no manager route. Monitoring the operations process and
368
+ managing application-owned Janus sessions are separate concerns.
369
+
370
+ The Admin surface is deliberately API-first (JSON, Prometheus text, and
371
+ WebSocket updates); core does not ship raw, uncompiled frontend source as a
372
+ production UI.
373
+
374
+ Admin, EventHandler, log viewer, and Timescale resources are disabled by
375
+ default. The service fails closed when an enabled component lacks credentials.
376
+ Schema migration is an explicit deployment action, never an import/startup side
377
+ effect:
378
+
379
+ ```python
380
+ import asyncio
381
+
382
+ from japi.contrib.admin.db import migrate
383
+
384
+ asyncio.run(migrate())
385
+ ```
386
+
387
+ EventHandler delivery uses the Broka abstraction with a stable
388
+ `janus-event-id` header, bounded per-application admission, finite publish and
389
+ batch deadlines, and partition/ordering keys derived from the Janus session or
390
+ emitter. Kafka deployments use the hardened core adapter, which forwards an
391
+ explicit allowlist of TLS/SASL and client options and requests idempotent,
392
+ `acks=all` delivery. Consumers should deduplicate by `janus-event-id` because an
393
+ HTTP batch can partially succeed before Janus retries it. Timescale queries have
394
+ statement, row, and time-bucket budgets; optional retention and compression
395
+ policies are installed only by the explicit migration.
396
+
397
+ Structured file logging is also opt-in. The handler installer is idempotent,
398
+ recursively redacts nested credentials, creates restrictive files, and supports
399
+ size-based or external watched rotation:
400
+
401
+ ```python
402
+ from jrtc.core.logging import install_colored_logging
403
+
404
+ install_colored_logging(logfile="/var/log/myapp/janus.jsonl", rotation="watched")
405
+ ```
406
+
407
+ ## Core configuration
408
+
409
+ Configure these values through the host application's environment or secret
410
+ manager. The package itself deliberately does not load `.env` files.
411
+
412
+ | Variable | Default | Purpose |
413
+ |---|---:|---|
414
+ | `JANUS_SESSION_URL` | `ws://localhost:8188/janus` | WS/WSS or HTTP/HTTPS API endpoint |
415
+ | `JANUS_REQUEST_TIMEOUT` | `15` | Per-request timeout in seconds |
416
+ | `JANUS_SESSION_POOL_SIZE` | `1` | Sessions per process |
417
+ | `JANUS_KEEPALIVE_INTERVAL` | `25` | WebSocket session keepalive interval |
418
+ | `JANUS_KEEPALIVE_FAILURES` | `3` | Failures before a session is invalidated |
419
+ | `JANUS_SHUTDOWN_TIMEOUT` | `10` | Total bounded session-shutdown budget |
420
+ | `JANUS_DETACH_CONCURRENCY` | `16` | Concurrent handle detach limit |
421
+ | `JANUS_TOKEN` | unset | Janus token authentication |
422
+ | `jrtc_SECRET` | unset | Janus shared API secret |
423
+ | `JANUS_BROKER_ENGINE` | `memory` | `memory`, `local`, `redis`, `rabbitmq`, or `kafka` |
424
+ | `JANUS_BROKER_ROUTE` | `janus.events` | Exact physical event destination |
425
+ | `JANUS_BROKER_ENGINE_OPTIONS` | `{}` | JSON object passed to the selected Broka engine |
426
+ | `JANUS_BROKER_OPTIONS` | `{}` | JSON object passed to Broka configuration |
427
+ | `JANUS_BROKER_PUBLISH_WORKERS` | `4` | Ordered publisher worker shards |
428
+ | `JANUS_BROKER_QUEUE_CAPACITY` | `4096` | Process-wide bounded event admission |
429
+ | `JANUS_BROKER_ADMISSION_TIMEOUT` | `0.05` | Maximum queue-admission wait in seconds |
430
+ | `JANUS_BROKER_PUBLISH_TIMEOUT` | `5` | Hard deadline for each backend publication |
431
+ | `JANUS_BROKER_DRAIN_TIMEOUT` | `10` | Publisher shutdown/drain budget in seconds |
432
+
433
+ Use a custom typed module through `JANUS_SETTINGS_MODULE` for more complex core
434
+ deployments; explicit overrides are available through `jrtc.conf.configure`.
435
+
436
+ ## Operations server configuration
437
+
438
+ The server loads its own settings independently through
439
+ `JANUS_SERVER_SETTINGS_MODULE` or `japi.configure`. Important
440
+ defaults are:
441
+
442
+ | Variable | Default | Purpose |
443
+ |---|---:|---|
444
+ | `JANUS_SERVER_MOUNT_REST_API` | `true` | Mount the REST application at `/janus` |
445
+ | `JANUS_SERVER_ENABLE_ADMIN` | `false` | Start Admin/Monitor integration |
446
+ | `JANUS_SERVER_ENABLE_EVENTS` | `false` | Start EventHandler broker ingestion |
447
+ | `JANUS_SERVER_MOUNT_LOGGING_APP` | `false` | Enable structured log query/streaming |
448
+ | `JANUS_SERVER_ALLOWED_ORIGINS` | unset | Exact CORS/WebSocket origins |
449
+ | `JANUS_SERVER_LIFESPAN_STARTUP_TIMEOUT` | `30` | Deadline for each external-resource startup |
450
+ | `JANUS_SERVER_LIFESPAN_SHUTDOWN_TIMEOUT` | `30` | Deadline for each external-resource shutdown |
451
+ | `JANUS_EVENT_BROKER_ENGINE` | unset | Required EventHandler Broka backend when ingestion is enabled |
452
+ | `JANUS_EVENT_BROKER_ENGINE_OPTIONS` | `{}` | Backend options, including secured Kafka options |
453
+ | `JANUS_EVENT_BROKER_OPTIONS` | `{}` | Broka reliability/serialization options |
454
+ | `JANUS_EVENT_HANDLER_LOGICAL_ROUTE` | `janus.eventhandler` | Logical Broka envelope type |
455
+ | `JANUS_EVENT_HANDLER_DESTINATION` | `janus.eventhandler` | Physical backend destination |
456
+ | `JANUS_EVENT_HANDLER_DELIVERY_CONCURRENCY` | `32` | Per-process concurrent deliveries |
457
+ | `JANUS_EVENT_HANDLER_MAX_INFLIGHT_BATCHES` | `16` | Per-process HTTP batch admission bound |
458
+ | `JANUS_EVENT_HANDLER_ADMISSION_TIMEOUT` | `0.1` | Capacity-admission deadline in seconds |
459
+ | `JANUS_EVENT_HANDLER_PUBLISH_TIMEOUT` | `5` | Per-event broker deadline in seconds |
460
+ | `JANUS_EVENT_HANDLER_BATCH_TIMEOUT` | `30` | Whole-batch deadline in seconds |
461
+ | `JANUS_TIMESCALE_QUERY_TIMEOUT` | `5` | Aggregate query/command timeout |
462
+ | `JANUS_TIMESCALE_MAX_QUERY_ROWS` | `10000` | Hard aggregate-result row limit |
463
+
464
+ Admin, EventHandler, log-viewer, and persistence credentials have no fallback
465
+ values. Enabled services fail startup when their required secrets are absent.
466
+ Redis Streams, RabbitMQ, or Kafka should back durable EventHandler ingestion;
467
+ Redis Pub/Sub is rejected because it cannot provide at-least-once delivery.
468
+
469
+ Scale the operations surfaces independently. EventHandler-only replicas are
470
+ stateless apart from bounded process-local admission and can scale horizontally
471
+ against one shared broker destination. Janus Admin polling, Timescale fallback
472
+ sampling, and file tailing are singleton activities for each Janus target or
473
+ log file: run those flags in a one-worker deployment (for example,
474
+ `uvicorn ... --workers 1`) or behind an external leader-election mechanism.
475
+ Enabling them in every worker would duplicate polling and persistence, while a
476
+ WebSocket can observe only the worker to which it is connected. Separate
477
+ event-only and admin/log deployments when both ingestion throughput and
478
+ monitoring availability need to scale.
479
+
480
+ ## Development and verification
481
+
482
+ ```bash
483
+ uv sync --all-packages --all-extras --group dev
484
+ uv run pytest
485
+ uv run ruff check src tests packages/japi/src packages/japi/tests
486
+ uv run ruff format --check src tests packages/japi/src packages/japi/tests
487
+ uv run mypy src/jrtc packages/japi/src/japi
488
+ uv build --package jrtc
489
+ uv build --package japi
490
+ ```
491
+
492
+ The tracked tests cover response validation, local plugin lifecycle, complete
493
+ ReactiveX removal, bounded publisher concurrency and draining, real Broka
494
+ memory delivery, Dispio response selection, and one-envelope JSEP behavior for
495
+ both built-in transports. Live Janus and external broker tests should also run
496
+ against the exact versions used in each deployment.
497
+
498
+ See the
499
+ [broker event guide](https://github.com/Leydotpy/Janus-API/blob/main/docs/broker-events.md)
500
+ for ownership, event contracts, backend subscription topology, reliability,
501
+ and deployment limits.
502
+
503
+ ## 3.1 messaging migration
504
+
505
+ - ReactiveX transport/plugin APIs were removed. Local plugin callbacks use
506
+ bounded instance-owned queues; cross-process subscribers use Broka.
507
+ - Dispio now coordinates ACK, error, transaction, and asynchronous response
508
+ handling without a transport `if`/`elif` decision tree.
509
+ - JSEP is delivered only inside its original response and is never dispatched
510
+ as a separate SDP event.
511
+ - Python 3.12 or newer is required by the pinned messaging dependencies.
512
+
513
+ ## 2.x migration notes
514
+
515
+ - Distribution: `janus-api` → `jrtc`; import namespace remains `jrtc`.
516
+ - `WebsocketSession` remains an alias of the transport-agnostic `JanusSession`.
517
+ - Named plugin models, clients, and the old VideoRoom facade moved out of core.
518
+ - Streaming moved completely to `janus-api-streaming` under `plugins`.
519
+ - Sessions and plugin managers are ordinary instances; process-global singleton
520
+ handles, global Rx event routing, eager plugin scanning, and Redis RPC are gone.
521
+ - Plugin payloads are no longer part of a closed core request/response union.
522
+ - Root logging, database migrations, threads, sockets, and optional-service
523
+ imports no longer happen at module import time.
524
+
525
+ ## Protocol references
526
+
527
+ - [Janus transports and core protocol](https://janus.conf.meetecho.com/docs/rest.html)
528
+ - [Janus API authentication](https://janus.conf.meetecho.com/docs/auth.html)
529
+ - [Admin/Monitor API](https://janus.conf.meetecho.com/docs/admin.html)
530
+ - [Event handlers](https://janus.conf.meetecho.com/docs/eventhandlers.html)
531
+ - [Recordings](https://janus.conf.meetecho.com/docs/recordings.html)
532
+
533
+ ## License
534
+
535
+ MIT