realtime-gateway 0.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.
- realtime_gateway-0.1.0/.gitignore +26 -0
- realtime_gateway-0.1.0/CHANGELOG.md +19 -0
- realtime_gateway-0.1.0/PKG-INFO +102 -0
- realtime_gateway-0.1.0/README.md +70 -0
- realtime_gateway-0.1.0/USAGE.md +196 -0
- realtime_gateway-0.1.0/docs/adr/0001-core-architecture.md +73 -0
- realtime_gateway-0.1.0/docs/adr/0002-system-goals-and-non-goals.md +49 -0
- realtime_gateway-0.1.0/docs/adr/0003-actors-and-system-roles.md +84 -0
- realtime_gateway-0.1.0/docs/adr/0004-project-terminology-and-glossary.md +54 -0
- realtime_gateway-0.1.0/docs/adr/0005-functional-requirements-specification.md +63 -0
- realtime_gateway-0.1.0/docs/adr/0006-non-functional-requirements-specification.md +58 -0
- realtime_gateway-0.1.0/docs/adr/0007-system-scope-and-boundary-interfaces.md +83 -0
- realtime_gateway-0.1.0/docs/adr/0008-primary-use-cases-and-workflows.md +72 -0
- realtime_gateway-0.1.0/docs/adr/0009-architecture-and-component-boundaries.md +125 -0
- realtime_gateway-0.1.0/docs/adr/0010-standardized-message-protocol-and-serialization.md +83 -0
- realtime_gateway-0.1.0/docs/adr/0011-websocket-library-selection.md +48 -0
- realtime_gateway-0.1.0/docs/adr/ADR-0011-reliability-and-reconnect-semantics.md +45 -0
- realtime_gateway-0.1.0/docs/adr/ADR-0012-security-controls-and-limits.md +59 -0
- realtime_gateway-0.1.0/docs/adr/ADR-0013-observability-logging-tracing-metrics.md +66 -0
- realtime_gateway-0.1.0/docs/adr/ADR-0014-testing-and-benchmarking-strategy.md +54 -0
- realtime_gateway-0.1.0/docs/adr/ADR-0015-message-broker-and-distributed-scale-out.md +45 -0
- realtime_gateway-0.1.0/docs/adr/ADR-0016-python-package-and-developer-experience.md +51 -0
- realtime_gateway-0.1.0/docs/adr/README.md +24 -0
- realtime_gateway-0.1.0/docs/architecture.md +48 -0
- realtime_gateway-0.1.0/docs/configuration.md +49 -0
- realtime_gateway-0.1.0/docs/deployment.md +80 -0
- realtime_gateway-0.1.0/docs/integration.md +53 -0
- realtime_gateway-0.1.0/docs/protocol.md +106 -0
- realtime_gateway-0.1.0/docs/release-checklist.md +47 -0
- realtime_gateway-0.1.0/docs/security.md +33 -0
- realtime_gateway-0.1.0/docs/websocket_requirements.md +41 -0
- realtime_gateway-0.1.0/example_client.py +26 -0
- realtime_gateway-0.1.0/example_usage.py +54 -0
- realtime_gateway-0.1.0/examples/asyncio_gateway.py +32 -0
- realtime_gateway-0.1.0/examples/fastapi_gateway.py +29 -0
- realtime_gateway-0.1.0/pyproject.toml +65 -0
- realtime_gateway-0.1.0/src/realtime_gateway/__init__.py +37 -0
- realtime_gateway-0.1.0/src/realtime_gateway/__version__.py +5 -0
- realtime_gateway-0.1.0/src/realtime_gateway/adapters/__init__.py +40 -0
- realtime_gateway-0.1.0/src/realtime_gateway/adapters/broker.py +227 -0
- realtime_gateway-0.1.0/src/realtime_gateway/adapters/jwt_authenticator.py +256 -0
- realtime_gateway-0.1.0/src/realtime_gateway/adapters/policy_authorizer.py +173 -0
- realtime_gateway-0.1.0/src/realtime_gateway/adapters/static_authenticator.py +91 -0
- realtime_gateway-0.1.0/src/realtime_gateway/adapters/websocket_transport.py +214 -0
- realtime_gateway-0.1.0/src/realtime_gateway/config/__init__.py +7 -0
- realtime_gateway-0.1.0/src/realtime_gateway/config/settings.py +49 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/__init__.py +56 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/auth.py +191 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/authorization.py +153 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/channel.py +32 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/cluster_router.py +78 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/connection.py +196 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/heartbeat.py +130 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/observability.py +169 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/principal.py +32 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/protocol.py +194 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/registry.py +176 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/reliability.py +272 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/routing.py +152 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/security.py +176 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/session.py +135 -0
- realtime_gateway-0.1.0/src/realtime_gateway/core/subscription.py +122 -0
- realtime_gateway-0.1.0/src/realtime_gateway/errors/__init__.py +65 -0
- realtime_gateway-0.1.0/src/realtime_gateway/gateway.py +188 -0
- realtime_gateway-0.1.0/src/realtime_gateway/interfaces/__init__.py +27 -0
- realtime_gateway-0.1.0/src/realtime_gateway/interfaces/authenticator.py +34 -0
- realtime_gateway-0.1.0/src/realtime_gateway/interfaces/authorizer.py +48 -0
- realtime_gateway-0.1.0/src/realtime_gateway/interfaces/broker.py +50 -0
- realtime_gateway-0.1.0/src/realtime_gateway/interfaces/serializer.py +95 -0
- realtime_gateway-0.1.0/src/realtime_gateway/interfaces/transport.py +93 -0
- realtime_gateway-0.1.0/src/realtime_gateway/observability/__init__.py +5 -0
- realtime_gateway-0.1.0/src/realtime_gateway/py.typed +1 -0
- realtime_gateway-0.1.0/src/realtime_gateway/runtime/__init__.py +5 -0
- realtime_gateway-0.1.0/src/realtime_gateway/security/__init__.py +5 -0
- realtime_gateway-0.1.0/tests/__init__.py +3 -0
- realtime_gateway-0.1.0/tests/adapters/__init__.py +3 -0
- realtime_gateway-0.1.0/tests/adapters/test_websocket_transport.py +166 -0
- realtime_gateway-0.1.0/tests/benchmarks/load_harness.py +103 -0
- realtime_gateway-0.1.0/tests/integration/test_multi_instance_broker.py +91 -0
- realtime_gateway-0.1.0/tests/integration/test_websocket_gateway_e2e.py +216 -0
- realtime_gateway-0.1.0/tests/unit/__init__.py +3 -0
- realtime_gateway-0.1.0/tests/unit/mock_transport.py +43 -0
- realtime_gateway-0.1.0/tests/unit/test_architecture_boundary.py +54 -0
- realtime_gateway-0.1.0/tests/unit/test_architecture_components.py +30 -0
- realtime_gateway-0.1.0/tests/unit/test_auth_manager.py +88 -0
- realtime_gateway-0.1.0/tests/unit/test_authorization.py +108 -0
- realtime_gateway-0.1.0/tests/unit/test_broker.py +111 -0
- realtime_gateway-0.1.0/tests/unit/test_connection_registry.py +135 -0
- realtime_gateway-0.1.0/tests/unit/test_connection_state.py +106 -0
- realtime_gateway-0.1.0/tests/unit/test_docs.py +54 -0
- realtime_gateway-0.1.0/tests/unit/test_heartbeat.py +83 -0
- realtime_gateway-0.1.0/tests/unit/test_jwt_authenticator.py +120 -0
- realtime_gateway-0.1.0/tests/unit/test_observability.py +93 -0
- realtime_gateway-0.1.0/tests/unit/test_package.py +73 -0
- realtime_gateway-0.1.0/tests/unit/test_principal.py +30 -0
- realtime_gateway-0.1.0/tests/unit/test_principal_auth.py +37 -0
- realtime_gateway-0.1.0/tests/unit/test_protocol_close.py +21 -0
- realtime_gateway-0.1.0/tests/unit/test_protocol_command.py +32 -0
- realtime_gateway-0.1.0/tests/unit/test_protocol_envelope.py +60 -0
- realtime_gateway-0.1.0/tests/unit/test_protocol_error.py +38 -0
- realtime_gateway-0.1.0/tests/unit/test_protocol_event.py +32 -0
- realtime_gateway-0.1.0/tests/unit/test_protocol_validation.py +41 -0
- realtime_gateway-0.1.0/tests/unit/test_protocol_versioning.py +30 -0
- realtime_gateway-0.1.0/tests/unit/test_reliability.py +169 -0
- realtime_gateway-0.1.0/tests/unit/test_routing.py +133 -0
- realtime_gateway-0.1.0/tests/unit/test_security.py +89 -0
- realtime_gateway-0.1.0/tests/unit/test_serializer.py +88 -0
- realtime_gateway-0.1.0/tests/unit/test_session.py +65 -0
- realtime_gateway-0.1.0/tests/unit/test_transport_interface.py +117 -0
- realtime_gateway-0.1.0/uv.lock +501 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
REALTIME_GATEWAY_MASTER.md
|
|
2
|
+
|
|
3
|
+
# Python
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*$py.class
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.eggs/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
|
|
12
|
+
# Environments
|
|
13
|
+
.venv/
|
|
14
|
+
env/
|
|
15
|
+
venv/
|
|
16
|
+
|
|
17
|
+
# Testing & Quality
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.mypy_cache/
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
.coverage
|
|
22
|
+
htmlcov/
|
|
23
|
+
|
|
24
|
+
# Local Project Metadata
|
|
25
|
+
.github_info
|
|
26
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-09-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Core Engine:** High-level `Gateway` orchestration facade and strongly-typed `GatewayConfig` system.
|
|
12
|
+
- **Transport Abstraction:** Decoupled `ITransport` and `ITransportConnection` contracts with `WebSocketTransport` implementation.
|
|
13
|
+
- **Connection Management:** `Connection` lifecycle state machine (`CONNECTING`, `AUTHENTICATING`, `CONNECTED`, `CLOSING`, `CLOSED`), `ConnectionRegistry`, and `SubscriptionManager`.
|
|
14
|
+
- **Message Protocol:** Standardized `MessageEnvelope` wire format supporting `COMMAND`, `EVENT`, `HEARTBEAT`, `ERROR`, and `DISCONNECT` frames.
|
|
15
|
+
- **Message Routing:** `MessageRouter` and `EventDispatcher` supporting Connection, User, Channel, and Broadcast routing targets.
|
|
16
|
+
- **Distributed Scale-Out:** Decoupled `IMessageBroker` abstraction with `InMemoryBroker` and `RedisBroker` adapters, connected via `BrokerEventBridge`.
|
|
17
|
+
- **Security & Limits:** `TokenBucketRateLimiter`, `ConnectionRateLimiter`, `MessageSizeGuard`, `ConnectionLimitGuard`, and `JWTAuthenticator`.
|
|
18
|
+
- **Reliability & Observability:** `ConnectionWriterQueue` backpressure management, `HeartbeatManager` zombie eviction, and `MetricsCollector` tracking.
|
|
19
|
+
- **Developer Experience:** PEP 561 `py.typed` compliance, optional PyPI extras (`[redis]`, `[jwt]`, `[websockets]`, `[all]`), and runnable Asyncio/FastAPI examples.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: realtime-gateway
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A reusable, transport-agnostic real-time WebSocket gateway package for Python
|
|
5
|
+
Author-email: Aryan Singla <aryansingla45@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: asyncio,gateway,pubsub,realtime,websocket
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Provides-Extra: all
|
|
17
|
+
Requires-Dist: pyjwt>=2.8.0; extra == 'all'
|
|
18
|
+
Requires-Dist: redis>=5.0.0; extra == 'all'
|
|
19
|
+
Requires-Dist: websockets>=12.0; extra == 'all'
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: mypy>=1.8.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff>=0.2.0; extra == 'dev'
|
|
25
|
+
Provides-Extra: jwt
|
|
26
|
+
Requires-Dist: pyjwt>=2.8.0; extra == 'jwt'
|
|
27
|
+
Provides-Extra: redis
|
|
28
|
+
Requires-Dist: redis>=5.0.0; extra == 'redis'
|
|
29
|
+
Provides-Extra: websockets
|
|
30
|
+
Requires-Dist: websockets>=12.0; extra == 'websockets'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Realtime Gateway
|
|
34
|
+
|
|
35
|
+
[](https://www.python.org/)
|
|
36
|
+
[](https://opensource.org/licenses/MIT)
|
|
37
|
+
[](https://github.com/astral-sh/ruff)
|
|
38
|
+
[](https://mypy-lang.org/)
|
|
39
|
+
|
|
40
|
+
**Realtime Gateway** is a high-performance, transport-agnostic real-time WebSocket gateway engine for Python. Built on modern `asyncio`, it provides decoupled connection handling, pub/sub subscription routing, token bucket rate limiting, authenticated channels, and horizontal scale-out via Redis Pub/Sub.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Key Features
|
|
45
|
+
|
|
46
|
+
- **Transport Agnostic Core**: Core engine runs independent of specific network protocols. Built-in `WebSocketTransport` adapter included.
|
|
47
|
+
- **Zero Core Dependencies**: Core installation requires zero third-party packages outside Python's standard library.
|
|
48
|
+
- **Pub/Sub Cluster Scale-Out**: Scale horizontally across multiple instances using `RedisBroker` or run single-instance with `InMemoryBroker`.
|
|
49
|
+
- **Built-in Security Guards**: Token bucket rate limiters, message/frame size guards, maximum connection limits, and anti-Slowloris connection eviction.
|
|
50
|
+
- **Authentication & Authorization**: Decoupled `IAuthenticator` (PyJWT support) and `IAuthorizer` interface contracts.
|
|
51
|
+
- **Observability Built-in**: `MetricsCollector` tracking active connections, message throughput, and rate limit drops.
|
|
52
|
+
- **Strictly Typed & PEP 561 Compliant**: 100% typed codebase with `py.typed` marker for downstream `mypy`/`pyright` validation.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Core install (zero third-party dependencies)
|
|
60
|
+
pip install realtime-gateway
|
|
61
|
+
|
|
62
|
+
# With all optional adapters (Redis, PyJWT, websockets)
|
|
63
|
+
pip install realtime-gateway[all]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 10-Line Quickstart
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import asyncio
|
|
72
|
+
from realtime_gateway import Gateway, GatewayConfig
|
|
73
|
+
|
|
74
|
+
async def main() -> None:
|
|
75
|
+
# Initialize gateway with strongly-typed configuration
|
|
76
|
+
config = GatewayConfig(host="127.0.0.1", port=8765, max_connections=1000)
|
|
77
|
+
gateway = Gateway(config=config)
|
|
78
|
+
|
|
79
|
+
print("Starting Realtime Gateway on ws://127.0.0.1:8765...")
|
|
80
|
+
await gateway.start()
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
asyncio.run(main())
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Documentation Guide
|
|
89
|
+
|
|
90
|
+
- [Architecture Guide](docs/architecture.md): System design, component interaction, and domain models.
|
|
91
|
+
- [Wire Protocol Specification](docs/protocol.md): JSON envelope formats, message types, and status codes.
|
|
92
|
+
- [Configuration Reference](docs/configuration.md): `GatewayConfig` parameters and environment variable overrides.
|
|
93
|
+
- [Framework Integration](docs/integration.md): Plugging into FastAPI, Django, and standalone Asyncio apps.
|
|
94
|
+
- [Production Deployment](docs/deployment.md): Horizontal scaling with Redis Pub/Sub behind load balancers.
|
|
95
|
+
- [Security Guidance](docs/security.md): TLS/WSS termination, token validation, and rate limit tuning.
|
|
96
|
+
- [ADR Index](docs/adr/README.md): Master index of all Architectural Decision Records (ADR-0001 to ADR-0016).
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
Distributed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Realtime Gateway
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://github.com/astral-sh/ruff)
|
|
6
|
+
[](https://mypy-lang.org/)
|
|
7
|
+
|
|
8
|
+
**Realtime Gateway** is a high-performance, transport-agnostic real-time WebSocket gateway engine for Python. Built on modern `asyncio`, it provides decoupled connection handling, pub/sub subscription routing, token bucket rate limiting, authenticated channels, and horizontal scale-out via Redis Pub/Sub.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Key Features
|
|
13
|
+
|
|
14
|
+
- **Transport Agnostic Core**: Core engine runs independent of specific network protocols. Built-in `WebSocketTransport` adapter included.
|
|
15
|
+
- **Zero Core Dependencies**: Core installation requires zero third-party packages outside Python's standard library.
|
|
16
|
+
- **Pub/Sub Cluster Scale-Out**: Scale horizontally across multiple instances using `RedisBroker` or run single-instance with `InMemoryBroker`.
|
|
17
|
+
- **Built-in Security Guards**: Token bucket rate limiters, message/frame size guards, maximum connection limits, and anti-Slowloris connection eviction.
|
|
18
|
+
- **Authentication & Authorization**: Decoupled `IAuthenticator` (PyJWT support) and `IAuthorizer` interface contracts.
|
|
19
|
+
- **Observability Built-in**: `MetricsCollector` tracking active connections, message throughput, and rate limit drops.
|
|
20
|
+
- **Strictly Typed & PEP 561 Compliant**: 100% typed codebase with `py.typed` marker for downstream `mypy`/`pyright` validation.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Core install (zero third-party dependencies)
|
|
28
|
+
pip install realtime-gateway
|
|
29
|
+
|
|
30
|
+
# With all optional adapters (Redis, PyJWT, websockets)
|
|
31
|
+
pip install realtime-gateway[all]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 10-Line Quickstart
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import asyncio
|
|
40
|
+
from realtime_gateway import Gateway, GatewayConfig
|
|
41
|
+
|
|
42
|
+
async def main() -> None:
|
|
43
|
+
# Initialize gateway with strongly-typed configuration
|
|
44
|
+
config = GatewayConfig(host="127.0.0.1", port=8765, max_connections=1000)
|
|
45
|
+
gateway = Gateway(config=config)
|
|
46
|
+
|
|
47
|
+
print("Starting Realtime Gateway on ws://127.0.0.1:8765...")
|
|
48
|
+
await gateway.start()
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
asyncio.run(main())
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Documentation Guide
|
|
57
|
+
|
|
58
|
+
- [Architecture Guide](docs/architecture.md): System design, component interaction, and domain models.
|
|
59
|
+
- [Wire Protocol Specification](docs/protocol.md): JSON envelope formats, message types, and status codes.
|
|
60
|
+
- [Configuration Reference](docs/configuration.md): `GatewayConfig` parameters and environment variable overrides.
|
|
61
|
+
- [Framework Integration](docs/integration.md): Plugging into FastAPI, Django, and standalone Asyncio apps.
|
|
62
|
+
- [Production Deployment](docs/deployment.md): Horizontal scaling with Redis Pub/Sub behind load balancers.
|
|
63
|
+
- [Security Guidance](docs/security.md): TLS/WSS termination, token validation, and rate limit tuning.
|
|
64
|
+
- [ADR Index](docs/adr/README.md): Master index of all Architectural Decision Records (ADR-0001 to ADR-0016).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
Distributed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Realtime Gateway - Developer Usage Guide
|
|
2
|
+
|
|
3
|
+
Welcome to the **Realtime Gateway**! This guide is designed to help you integrate and use this package for your real-time use cases.
|
|
4
|
+
|
|
5
|
+
## What is this?
|
|
6
|
+
|
|
7
|
+
The Realtime Gateway is a production-ready, protocol-agnostic WebSocket gateway. It manages connection lifecycles, pub/sub channels, security limits, scaling via brokers, and heartbeats.
|
|
8
|
+
|
|
9
|
+
Instead of re-inventing WebSocket connection management in every web service (FastAPI, Django, etc.), you can embed this gateway into your application or run it as a standalone service.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Installation
|
|
14
|
+
|
|
15
|
+
The package can be installed with optional dependencies depending on your needs. For local development, you can use:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv pip install -e .[all]
|
|
19
|
+
```
|
|
20
|
+
*(Available extras: `[redis]`, `[jwt]`, `[websockets]`, `[all]`)*
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 2. Basic Standalone Usage (Asyncio)
|
|
25
|
+
|
|
26
|
+
The core entry point is the `Gateway` class, which orchestrates everything. You can configure it using `GatewayConfig`.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import asyncio
|
|
30
|
+
from realtime_gateway import Gateway, GatewayConfig
|
|
31
|
+
|
|
32
|
+
async def main():
|
|
33
|
+
# 1. Define configuration (limits, ports, timeouts)
|
|
34
|
+
config = GatewayConfig(
|
|
35
|
+
host="127.0.0.1",
|
|
36
|
+
port=8765,
|
|
37
|
+
max_connections=10_000,
|
|
38
|
+
rate_limit_rps=20.0,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# 2. Initialize the Gateway
|
|
42
|
+
gateway = Gateway(config=config)
|
|
43
|
+
|
|
44
|
+
# 3. Start it up
|
|
45
|
+
print(f"Gateway listening on ws://{config.host}:{config.port}")
|
|
46
|
+
await gateway.start()
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
# Keep process alive
|
|
50
|
+
await asyncio.Event().wait()
|
|
51
|
+
finally:
|
|
52
|
+
# 4. Clean shutdown
|
|
53
|
+
await gateway.stop()
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
asyncio.run(main())
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 3. Integrating with FastAPI (or other ASGI frameworks)
|
|
62
|
+
|
|
63
|
+
You don't need a separate process to run the gateway; it can run concurrently in the same event loop as your API server!
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from contextlib import asynccontextmanager
|
|
67
|
+
from fastapi import FastAPI
|
|
68
|
+
from realtime_gateway import Gateway, GatewayConfig
|
|
69
|
+
|
|
70
|
+
@asynccontextmanager
|
|
71
|
+
async def lifespan(app: FastAPI):
|
|
72
|
+
# Initialize gateway from environment variables
|
|
73
|
+
config = GatewayConfig.from_env()
|
|
74
|
+
gateway = Gateway(config=config)
|
|
75
|
+
|
|
76
|
+
# Attach to app state if your HTTP routes need to interact with it
|
|
77
|
+
app.state.gateway = gateway
|
|
78
|
+
|
|
79
|
+
# Start gateway
|
|
80
|
+
await gateway.start()
|
|
81
|
+
|
|
82
|
+
yield # Hand over to FastAPI application
|
|
83
|
+
|
|
84
|
+
# Stop cleanly when FastAPI shuts down
|
|
85
|
+
await gateway.stop()
|
|
86
|
+
|
|
87
|
+
app = FastAPI(lifespan=lifespan)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 4. The Message Protocol
|
|
93
|
+
|
|
94
|
+
The gateway expects messages from the client to be sent in a strict envelope format (JSON). The base format is:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"version": "1",
|
|
99
|
+
"id": "msg-1234",
|
|
100
|
+
"type": "command",
|
|
101
|
+
"payload": {
|
|
102
|
+
"action": "...",
|
|
103
|
+
"...": "..."
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Built-in Actions: Subscribe & Publish
|
|
109
|
+
|
|
110
|
+
The gateway has built-in routing for pub/sub via channels.
|
|
111
|
+
|
|
112
|
+
**Subscribing to a channel:**
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"type": "command",
|
|
116
|
+
"payload": {
|
|
117
|
+
"action": "subscribe",
|
|
118
|
+
"channel": "notifications:user-123"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Publishing a message to a channel:**
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"type": "command",
|
|
127
|
+
"payload": {
|
|
128
|
+
"action": "publish",
|
|
129
|
+
"channel": "notifications:user-123",
|
|
130
|
+
"message": {
|
|
131
|
+
"alert": "Your report is ready!"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
*Note: Any client subscribed to `notifications:user-123` will instantly receive the `publish` payload as an `event` message.*
|
|
137
|
+
|
|
138
|
+
**Heartbeats:**
|
|
139
|
+
To prevent the gateway from closing inactive connections, clients must occasionally send ping frames:
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"type": "command",
|
|
143
|
+
"payload": {
|
|
144
|
+
"action": "ping"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 5. Scaling Out (Multi-Instance)
|
|
152
|
+
|
|
153
|
+
By default, the `Gateway` uses an `InMemoryBroker`. This means clients connected to Instance A cannot publish messages to clients on Instance B.
|
|
154
|
+
|
|
155
|
+
To solve this, inject the `RedisBroker`. The `Gateway` accepts injected adapters.
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from realtime_gateway import Gateway
|
|
159
|
+
from realtime_gateway.adapters.broker.redis import RedisBroker
|
|
160
|
+
|
|
161
|
+
async def main():
|
|
162
|
+
# Setup Redis Broker
|
|
163
|
+
broker = RedisBroker(redis_url="redis://localhost:6379")
|
|
164
|
+
|
|
165
|
+
# Inject it into the Gateway
|
|
166
|
+
gateway = Gateway(broker=broker)
|
|
167
|
+
|
|
168
|
+
await gateway.start()
|
|
169
|
+
```
|
|
170
|
+
Now, if Client A connects to Gateway A and publishes to `chat:lobby`, the message goes to Redis, which forwards it to Gateway B, which delivers it to Client B.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 6. Extending the Gateway (Custom Authorization)
|
|
175
|
+
|
|
176
|
+
If you need a custom way to verify user tokens or set permissions, you can implement the `IAuthenticator` interface and inject it exactly like the broker above:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from realtime_gateway import Gateway
|
|
180
|
+
from realtime_gateway.interfaces.authenticator import IAuthenticator
|
|
181
|
+
|
|
182
|
+
class MyCustomAuth(IAuthenticator):
|
|
183
|
+
async def authenticate(self, credentials):
|
|
184
|
+
# your custom logic here
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
gateway = Gateway(authenticator=MyCustomAuth())
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Summary for Developers
|
|
193
|
+
1. **Don't touch the TCP/WebSocket layer.** The `WebSocketTransport` handles it.
|
|
194
|
+
2. **Don't hardcode Redis/RabbitMQ.** Inject the `IMessageBroker` if needed.
|
|
195
|
+
3. **Use the `Gateway` facade.** It wraps rate limiting, metrics, heartbeats, and message routing.
|
|
196
|
+
4. **Follow the protocol.** Use the `MessageEnvelope` structure on the frontend.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# ADR-001: Core Architecture & Infrastructure Decoupling
|
|
2
|
+
|
|
3
|
+
* **Status:** Accepted
|
|
4
|
+
* **Date:** 2026-08-24
|
|
5
|
+
* **Deciders:** Realtime Gateway Architecture Team
|
|
6
|
+
* **Parent Epic:** EPIC-01 — System Definition
|
|
7
|
+
* **Ticket:** #2 — Write problem statement & system decision
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Context and Problem Statement
|
|
12
|
+
|
|
13
|
+
Real-time applications requiring persistent bidirectional communication often tightly couple their business logic to specific WebSocket libraries, HTTP frameworks (e.g., FastAPI, Starlette), pub/sub message brokers (e.g., Redis, RabbitMQ), or authentication providers (e.g., PyJWT).
|
|
14
|
+
|
|
15
|
+
When these infrastructure technologies evolve or change, or when another Python project attempts to reuse the real-time gateway capability, the tight coupling forces costly refactoring or framework lock-in.
|
|
16
|
+
|
|
17
|
+
We need an architectural strategy that guarantees the gateway core remains decoupled from external transport, broker, and authentication technologies while delivering high performance, reliability, and clean package reusability.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Decision Drivers
|
|
22
|
+
|
|
23
|
+
1. **Reusability:** The gateway must be installable as a standalone Python package (`realtime-gateway`) in any downstream Python application.
|
|
24
|
+
2. **Framework Independence:** Core domain logic must not import or depend on FastAPI, Django, Redis, PyJWT, or specific WebSocket server implementations.
|
|
25
|
+
3. **Maintainability & Testability:** Core components must be testable in memory using standard Python `asyncio` without requiring running Redis servers or active network sockets.
|
|
26
|
+
4. **Security & Liveness:** Architectural boundaries must enforce strict backpressure, rate limits, and heartbeat management.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Considered Options
|
|
31
|
+
|
|
32
|
+
1. **Option 1: Direct Framework Integration (FastAPI/Starlette + Redis)**
|
|
33
|
+
- *Pros:* Faster initial prototype.
|
|
34
|
+
- *Cons:* Locks the package into FastAPI/Starlette; forces consumers to adopt Redis even for single-instance deployments; hard to test core logic in isolation.
|
|
35
|
+
|
|
36
|
+
2. **Option 2: Hexagonal / Ports-and-Adapters Architecture (Chosen)**
|
|
37
|
+
- *Pros:* Complete decoupling of core logic from infrastructure details. Transport (`Transport`), Broker (`MessageBroker`), Authentication (`Authenticator`), and Authorization (`Authorizer`) are abstract interfaces. Core owns domain logic (`ConnectionManager`, `Router`, `SubscriptionManager`, `Gateway`).
|
|
38
|
+
- *Cons:* Requires establishing explicit abstract interfaces upfront.
|
|
39
|
+
|
|
40
|
+
3. **Option 3: Custom Protocols over Raw Sockets**
|
|
41
|
+
- *Pros:* Total low-level control.
|
|
42
|
+
- *Cons:* Reinventing TCP/TLS/WebSocket framing creates immense maintenance burden and security risks without adding gateway domain value.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Decision Outcome
|
|
47
|
+
|
|
48
|
+
Chosen Option: **Option 2 (Hexagonal Architecture)**.
|
|
49
|
+
|
|
50
|
+
### Architecture Rules
|
|
51
|
+
1. `src/realtime_gateway/core/` owns domain entities (`Connection`, `Principal`, `Message`, `Subscription`) and domain logic (`ConnectionManager`, `Router`). Core files MUST NOT import external infrastructure packages.
|
|
52
|
+
2. `src/realtime_gateway/interfaces/` defines abstract `typing.Protocol` or `abc.ABC` contracts for all external interactions (`Transport`, `MessageBroker`, `Authenticator`, `Authorizer`, `Serializer`, `Observer`).
|
|
53
|
+
3. `src/realtime_gateway/adapters/` houses concrete technology implementations (e.g., WebSocket transport, Redis pub/sub broker, JWT authentication). Adapters are optional dependencies.
|
|
54
|
+
4. The core package MUST be usable out of the box with zero required external binary dependencies.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Consequences
|
|
59
|
+
|
|
60
|
+
### Positive
|
|
61
|
+
* **Zero Leakage:** Infrastructure choices do not leak into gateway state management.
|
|
62
|
+
* **Testability:** 100% of core routing, connection state machine, sub/pub, and backpressure logic can be unit-tested cleanly in memory without network IO.
|
|
63
|
+
* **Pluggability:** Downstream applications can inject custom authenticators, brokers, or transports without modifying package internals.
|
|
64
|
+
|
|
65
|
+
### Negative / Trade-offs
|
|
66
|
+
* Requires careful maintenance of interface contracts (`interfaces/`).
|
|
67
|
+
* Adapters must strictly convert third-party exceptions and data types to core gateway representations.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Compliance Verification
|
|
72
|
+
|
|
73
|
+
Automated architecture tests (`tests/unit/test_architecture_boundary.py`) will continuously verify that `realtime_gateway.core` and `realtime_gateway.interfaces` maintain zero imports from concrete infrastructure libraries (`redis`, `websockets`, `jwt`, `fastapi`, `starlette`).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ADR-002: System Goals, System Boundaries, and Non-Goals
|
|
2
|
+
|
|
3
|
+
* **Status:** Accepted
|
|
4
|
+
* **Date:** 2026-08-24
|
|
5
|
+
* **Deciders:** Realtime Gateway Architecture Team
|
|
6
|
+
* **Parent Epic:** EPIC-01 — System Definition
|
|
7
|
+
* **Ticket:** #3 — Define goals and non-goals
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Context and Problem Statement
|
|
12
|
+
|
|
13
|
+
Real-time communication engines frequently suffer from scope creep. Without explicit architectural boundaries, a real-time gateway risk evolving into a bloated monolith—attempting to serve as an HTTP web framework, a persistent database, an identity provider (OAuth/JWT issuer), a TCP wire-protocol engine, or a full API gateway.
|
|
14
|
+
|
|
15
|
+
To maintain a focused, high-performance, and reusable Python package (`realtime-gateway`), we must formally define the explicit goals (what the gateway MUST do) and non-goals (what the gateway MUST NOT do).
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## System Goals (In Scope)
|
|
20
|
+
|
|
21
|
+
1. **Persistent Connection Management:** Track, maintain, and transition WebSocket client connection lifecycles (`CONNECTING`, `AUTHENTICATING`, `CONNECTED`, `CLOSING`, `CLOSED`).
|
|
22
|
+
2. **Abstract Authentication & Authorization Boundaries:** Intercept client connections and message actions via replaceable `Authenticator` and `Authorizer` interfaces.
|
|
23
|
+
3. **Flexible Sub/Pub Routing:** Support direct connection targeting, user-targeted delivery, channel broadcasting, and multicast routing.
|
|
24
|
+
4. **Reliability & Resource Protection:** Bounded outgoing queues, backpressure handling, idle/pong heartbeat timeouts, and dead connection cleanup.
|
|
25
|
+
5. **Horizontal Scalability:** Support multi-instance gateway clusters via a pluggable `MessageBroker` abstraction (In-Memory and Redis Pub/Sub).
|
|
26
|
+
6. **Observability:** Structured lifecycle event logging, metric counters/histograms, and correlation ID tracing.
|
|
27
|
+
7. **Clean Package Interface:** Minimal core dependencies, installable via `uv`/`pip` into any downstream Python codebase.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Non-Goals (Explicitly Out of Scope)
|
|
32
|
+
|
|
33
|
+
1. **Not an HTTP Application Server:** The gateway does not handle general REST API routing, static file serving, or general HTTP requests.
|
|
34
|
+
2. **Not a TCP/WebSocket Wire Protocol Implementation:** Wire-level framing, TLS termination, and socket masking are delegated to mature WebSocket transport libraries behind our `Transport` adapter boundary.
|
|
35
|
+
3. **Not a Message Broker / Storage Replacement:** The gateway is a real-time router, not a persistent queue or storage database (e.g., Redis, RabbitMQ, Kafka, Postgres).
|
|
36
|
+
4. **Not an Identity Provider:** The gateway validates tokens via an `Authenticator` interface but does not issue tokens, manage user passwords, or act as an OAuth server.
|
|
37
|
+
5. **Not a General Service Mesh or HTTP API Gateway:** The gateway strictly handles real-time traffic control; it does not replace Envoy, Kong, or NGINX for REST/gRPC microservices traffic.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Consequences
|
|
42
|
+
|
|
43
|
+
### Positive
|
|
44
|
+
- Prevents bloat and keeps `realtime-gateway` core clean, fast, and easy to maintain.
|
|
45
|
+
- Downstream applications can integrate the gateway without replacing their existing HTTP web frameworks or authentication servers.
|
|
46
|
+
- Clear test boundaries: tests focus on real-time traffic control, routing, state management, and backpressure.
|
|
47
|
+
|
|
48
|
+
### Negative / Trade-offs
|
|
49
|
+
- Downstream applications must provide or plug in their own authentication token issuers and HTTP servers.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# ADR-003: Actors, System Roles, and Principal Identity Boundaries
|
|
2
|
+
|
|
3
|
+
* **Status:** Accepted
|
|
4
|
+
* **Date:** 2026-08-24
|
|
5
|
+
* **Deciders:** Realtime Gateway Architecture Team
|
|
6
|
+
* **Parent Epic:** EPIC-01 — System Definition
|
|
7
|
+
* **Ticket:** #4 — Identify actors and system roles
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Context and Problem Statement
|
|
12
|
+
|
|
13
|
+
A real-time gateway operates at the boundary between external clients (browsers, mobile apps), backend application services, and underlying infrastructure (WebSocket transports, pub/sub brokers).
|
|
14
|
+
|
|
15
|
+
Without a clean taxonomy of actors, system roles, and identity representations, system designs risk blurring boundaries—such as coupling the gateway's connection registry directly to a specific application's `User` database model or allowing unauthorized client actors to broadcast arbitrary messages across channels.
|
|
16
|
+
|
|
17
|
+
We need to explicitly define the system actors, operational roles, and the generic `Principal` identity contract.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Actor Taxonomy
|
|
22
|
+
|
|
23
|
+
| Actor | Type | Description |
|
|
24
|
+
| :--- | :--- | :--- |
|
|
25
|
+
| **External Client** | Human / Device / Client App | Connects via WebSocket/Transport to send commands and receive real-time events. |
|
|
26
|
+
| **Consuming Application** | Backend Service | Embeds `realtime-gateway` package, authenticates/authorizes clients, and dispatches domain events. |
|
|
27
|
+
| **Gateway Core Engine** | System Component | Internal orchestrator managing connection state machine, router tables, heartbeat liveness, and backpressure. |
|
|
28
|
+
| **Infrastructure Adapters** | External Drivers | Pluggable implementations (`Transport`, `MessageBroker`, `Authenticator`, `Authorizer`) bridging technology boundaries. |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## System Roles & Identity States
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
┌──────────────────────────┐
|
|
36
|
+
│ Anonymous Connection │
|
|
37
|
+
└────────────┬─────────────┘
|
|
38
|
+
│ Authenticates
|
|
39
|
+
▼
|
|
40
|
+
┌──────────────────────────┐
|
|
41
|
+
│ Authenticated Principal │
|
|
42
|
+
└────────────┬─────────────┘
|
|
43
|
+
│ Subscribes
|
|
44
|
+
▼
|
|
45
|
+
┌──────────────────────────┐
|
|
46
|
+
│ Channel Subscriber │
|
|
47
|
+
└──────────────────────────┘
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
1. **Anonymous Connection:** An unauthenticated connection state (`CONNECTING` / `AUTHENTICATING`). Can only send initial credentials or heartbeat ping/pong frames; cannot subscribe or broadcast.
|
|
51
|
+
2. **Authenticated Principal:** A connection associated with a validated identity (`Principal`). Can hold multiple active connections across devices.
|
|
52
|
+
3. **Channel Subscriber:** An authenticated connection registered to receive events for specific logical channels (e.g. `orders:123`, `user:456`).
|
|
53
|
+
4. **Message Publisher:** An actor (consuming application or authorized client) permitted by `Authorizer` to push events to target connections or channels.
|
|
54
|
+
5. **Gateway Cluster Instance:** An individual node in a scaled gateway deployment communicating cross-instance events via a shared message broker topic.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Principal Identity Contract
|
|
59
|
+
|
|
60
|
+
The gateway MUST NOT depend on any consuming application's User model, database schema, or auth framework.
|
|
61
|
+
|
|
62
|
+
Identity is represented by an immutable domain model:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class Principal:
|
|
67
|
+
id: str
|
|
68
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- **`id`**: Unique string identifier representing the user, device, or service.
|
|
72
|
+
- **`metadata`**: Immutable mapping holding tenant IDs, roles, scopes, or claims required by downstream authorizers.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Consequences
|
|
77
|
+
|
|
78
|
+
### Positive
|
|
79
|
+
- **Decoupled User Model:** The gateway operates seamlessly with any user system (SQL databases, Auth0, Firebase, custom microservices).
|
|
80
|
+
- **Strict Role Boundaries:** Clear separation between connection-level state and authenticated principal identity.
|
|
81
|
+
- **Multi-Device Support:** A single `Principal` can own multiple concurrent connections (`for_user(user_id)` mapping).
|
|
82
|
+
|
|
83
|
+
### Negative / Trade-offs
|
|
84
|
+
- Downstream applications must map their internal user objects to the gateway's `Principal` during authentication.
|