@mt-tl/server 0.1.0

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 (293) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/auth/handshake.d.ts +35 -0
  4. package/dist/auth/handshake.d.ts.map +1 -0
  5. package/dist/auth/handshake.js +208 -0
  6. package/dist/auth/handshake.js.map +1 -0
  7. package/dist/auth/nonce-store.d.ts +22 -0
  8. package/dist/auth/nonce-store.d.ts.map +1 -0
  9. package/dist/auth/nonce-store.js +23 -0
  10. package/dist/auth/nonce-store.js.map +1 -0
  11. package/dist/bootstrap.d.ts +36 -0
  12. package/dist/bootstrap.d.ts.map +1 -0
  13. package/dist/bootstrap.js +82 -0
  14. package/dist/bootstrap.js.map +1 -0
  15. package/dist/config.d.ts +103 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +2 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/core/context.d.ts +57 -0
  20. package/dist/core/context.d.ts.map +1 -0
  21. package/dist/core/context.js +27 -0
  22. package/dist/core/context.js.map +1 -0
  23. package/dist/core/errors.d.ts +33 -0
  24. package/dist/core/errors.d.ts.map +1 -0
  25. package/dist/core/errors.js +47 -0
  26. package/dist/core/errors.js.map +1 -0
  27. package/dist/core/index.d.ts +5 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +5 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/rpc.d.ts +83 -0
  32. package/dist/core/rpc.d.ts.map +1 -0
  33. package/dist/core/rpc.js +102 -0
  34. package/dist/core/rpc.js.map +1 -0
  35. package/dist/core/updates.d.ts +56 -0
  36. package/dist/core/updates.d.ts.map +1 -0
  37. package/dist/core/updates.js +34 -0
  38. package/dist/core/updates.js.map +1 -0
  39. package/dist/create-server.d.ts +89 -0
  40. package/dist/create-server.d.ts.map +1 -0
  41. package/dist/create-server.js +109 -0
  42. package/dist/create-server.js.map +1 -0
  43. package/dist/crypto/aes-ige.d.ts +3 -0
  44. package/dist/crypto/aes-ige.d.ts.map +1 -0
  45. package/dist/crypto/aes-ige.js +55 -0
  46. package/dist/crypto/aes-ige.js.map +1 -0
  47. package/dist/crypto/dh.d.ts +21 -0
  48. package/dist/crypto/dh.d.ts.map +1 -0
  49. package/dist/crypto/dh.js +99 -0
  50. package/dist/crypto/dh.js.map +1 -0
  51. package/dist/crypto/hashes.d.ts +6 -0
  52. package/dist/crypto/hashes.d.ts.map +1 -0
  53. package/dist/crypto/hashes.js +14 -0
  54. package/dist/crypto/hashes.js.map +1 -0
  55. package/dist/crypto/msg-key.d.ts +15 -0
  56. package/dist/crypto/msg-key.d.ts.map +1 -0
  57. package/dist/crypto/msg-key.js +24 -0
  58. package/dist/crypto/msg-key.js.map +1 -0
  59. package/dist/crypto/rsa.d.ts +27 -0
  60. package/dist/crypto/rsa.d.ts.map +1 -0
  61. package/dist/crypto/rsa.js +50 -0
  62. package/dist/crypto/rsa.js.map +1 -0
  63. package/dist/dispatch/dispatcher.d.ts +72 -0
  64. package/dist/dispatch/dispatcher.d.ts.map +1 -0
  65. package/dist/dispatch/dispatcher.js +503 -0
  66. package/dist/dispatch/dispatcher.js.map +1 -0
  67. package/dist/dispatch/forwarders/in-process.d.ts +12 -0
  68. package/dist/dispatch/forwarders/in-process.d.ts.map +1 -0
  69. package/dist/dispatch/forwarders/in-process.js +15 -0
  70. package/dist/dispatch/forwarders/in-process.js.map +1 -0
  71. package/dist/dispatch/forwarders/print.d.ts +14 -0
  72. package/dist/dispatch/forwarders/print.d.ts.map +1 -0
  73. package/dist/dispatch/forwarders/print.js +23 -0
  74. package/dist/dispatch/forwarders/print.js.map +1 -0
  75. package/dist/dispatch/rpc-forwarder.d.ts +14 -0
  76. package/dist/dispatch/rpc-forwarder.d.ts.map +1 -0
  77. package/dist/dispatch/rpc-forwarder.js +2 -0
  78. package/dist/dispatch/rpc-forwarder.js.map +1 -0
  79. package/dist/dispatch/types.d.ts +32 -0
  80. package/dist/dispatch/types.d.ts.map +1 -0
  81. package/dist/dispatch/types.js +23 -0
  82. package/dist/dispatch/types.js.map +1 -0
  83. package/dist/gateway.d.ts +57 -0
  84. package/dist/gateway.d.ts.map +1 -0
  85. package/dist/gateway.js +150 -0
  86. package/dist/gateway.js.map +1 -0
  87. package/dist/index.d.ts +7 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +25 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/lib.d.ts +12 -0
  92. package/dist/lib.d.ts.map +1 -0
  93. package/dist/lib.js +13 -0
  94. package/dist/lib.js.map +1 -0
  95. package/dist/server/message-pipeline.d.ts +57 -0
  96. package/dist/server/message-pipeline.d.ts.map +1 -0
  97. package/dist/server/message-pipeline.js +199 -0
  98. package/dist/server/message-pipeline.js.map +1 -0
  99. package/dist/session/inbound-tracker.d.ts +118 -0
  100. package/dist/session/inbound-tracker.d.ts.map +1 -0
  101. package/dist/session/inbound-tracker.js +170 -0
  102. package/dist/session/inbound-tracker.js.map +1 -0
  103. package/dist/session/message-id.d.ts +19 -0
  104. package/dist/session/message-id.d.ts.map +1 -0
  105. package/dist/session/message-id.js +30 -0
  106. package/dist/session/message-id.js.map +1 -0
  107. package/dist/session/salts.d.ts +51 -0
  108. package/dist/session/salts.d.ts.map +1 -0
  109. package/dist/session/salts.js +132 -0
  110. package/dist/session/salts.js.map +1 -0
  111. package/dist/session/session-manager.d.ts +18 -0
  112. package/dist/session/session-manager.d.ts.map +1 -0
  113. package/dist/session/session-manager.js +43 -0
  114. package/dist/session/session-manager.js.map +1 -0
  115. package/dist/storage/index.d.ts +14 -0
  116. package/dist/storage/index.d.ts.map +1 -0
  117. package/dist/storage/index.js +16 -0
  118. package/dist/storage/index.js.map +1 -0
  119. package/dist/storage/memory.d.ts +3 -0
  120. package/dist/storage/memory.d.ts.map +1 -0
  121. package/dist/storage/memory.js +91 -0
  122. package/dist/storage/memory.js.map +1 -0
  123. package/dist/storage/mongo.d.ts +3 -0
  124. package/dist/storage/mongo.d.ts.map +1 -0
  125. package/dist/storage/mongo.js +175 -0
  126. package/dist/storage/mongo.js.map +1 -0
  127. package/dist/storage/types.d.ts +85 -0
  128. package/dist/storage/types.d.ts.map +1 -0
  129. package/dist/storage/types.js +3 -0
  130. package/dist/storage/types.js.map +1 -0
  131. package/dist/testkit.d.ts +11 -0
  132. package/dist/testkit.d.ts.map +1 -0
  133. package/dist/testkit.js +17 -0
  134. package/dist/testkit.js.map +1 -0
  135. package/dist/tl/codec.d.ts +37 -0
  136. package/dist/tl/codec.d.ts.map +1 -0
  137. package/dist/tl/codec.js +297 -0
  138. package/dist/tl/codec.js.map +1 -0
  139. package/dist/tl/layered-registry.d.ts +29 -0
  140. package/dist/tl/layered-registry.d.ts.map +1 -0
  141. package/dist/tl/layered-registry.js +118 -0
  142. package/dist/tl/layered-registry.js.map +1 -0
  143. package/dist/tl/protocol.d.ts +126 -0
  144. package/dist/tl/protocol.d.ts.map +1 -0
  145. package/dist/tl/protocol.js +22 -0
  146. package/dist/tl/protocol.js.map +1 -0
  147. package/dist/tl/reader.d.ts +30 -0
  148. package/dist/tl/reader.d.ts.map +1 -0
  149. package/dist/tl/reader.js +87 -0
  150. package/dist/tl/reader.js.map +1 -0
  151. package/dist/tl/registry.d.ts +39 -0
  152. package/dist/tl/registry.d.ts.map +1 -0
  153. package/dist/tl/registry.js +52 -0
  154. package/dist/tl/registry.js.map +1 -0
  155. package/dist/tl/writer.d.ts +24 -0
  156. package/dist/tl/writer.d.ts.map +1 -0
  157. package/dist/tl/writer.js +95 -0
  158. package/dist/tl/writer.js.map +1 -0
  159. package/dist/transport/connection-registry.d.ts +31 -0
  160. package/dist/transport/connection-registry.d.ts.map +1 -0
  161. package/dist/transport/connection-registry.js +84 -0
  162. package/dist/transport/connection-registry.js.map +1 -0
  163. package/dist/transport/connection.d.ts +62 -0
  164. package/dist/transport/connection.d.ts.map +1 -0
  165. package/dist/transport/connection.js +77 -0
  166. package/dist/transport/connection.js.map +1 -0
  167. package/dist/transport/framing.d.ts +39 -0
  168. package/dist/transport/framing.d.ts.map +1 -0
  169. package/dist/transport/framing.js +212 -0
  170. package/dist/transport/framing.js.map +1 -0
  171. package/dist/transport/proxy-protocol.d.ts +23 -0
  172. package/dist/transport/proxy-protocol.d.ts.map +1 -0
  173. package/dist/transport/proxy-protocol.js +108 -0
  174. package/dist/transport/proxy-protocol.js.map +1 -0
  175. package/dist/transport/server-common.d.ts +27 -0
  176. package/dist/transport/server-common.d.ts.map +1 -0
  177. package/dist/transport/server-common.js +27 -0
  178. package/dist/transport/server-common.js.map +1 -0
  179. package/dist/transport/tcp-server.d.ts +26 -0
  180. package/dist/transport/tcp-server.d.ts.map +1 -0
  181. package/dist/transport/tcp-server.js +91 -0
  182. package/dist/transport/tcp-server.js.map +1 -0
  183. package/dist/transport/ws-server.d.ts +19 -0
  184. package/dist/transport/ws-server.d.ts.map +1 -0
  185. package/dist/transport/ws-server.js +78 -0
  186. package/dist/transport/ws-server.js.map +1 -0
  187. package/dist/update-publisher.d.ts +34 -0
  188. package/dist/update-publisher.d.ts.map +1 -0
  189. package/dist/update-publisher.js +29 -0
  190. package/dist/update-publisher.js.map +1 -0
  191. package/dist/updates/mongo-update-log.d.ts +13 -0
  192. package/dist/updates/mongo-update-log.d.ts.map +1 -0
  193. package/dist/updates/mongo-update-log.js +39 -0
  194. package/dist/updates/mongo-update-log.js.map +1 -0
  195. package/dist/updates/presence-binder.d.ts +29 -0
  196. package/dist/updates/presence-binder.d.ts.map +1 -0
  197. package/dist/updates/presence-binder.js +36 -0
  198. package/dist/updates/presence-binder.js.map +1 -0
  199. package/dist/updates/presence.d.ts +31 -0
  200. package/dist/updates/presence.d.ts.map +1 -0
  201. package/dist/updates/presence.js +44 -0
  202. package/dist/updates/presence.js.map +1 -0
  203. package/dist/updates/push.d.ts +25 -0
  204. package/dist/updates/push.d.ts.map +1 -0
  205. package/dist/updates/push.js +72 -0
  206. package/dist/updates/push.js.map +1 -0
  207. package/dist/updates/redis-bus.d.ts +45 -0
  208. package/dist/updates/redis-bus.d.ts.map +1 -0
  209. package/dist/updates/redis-bus.js +59 -0
  210. package/dist/updates/redis-bus.js.map +1 -0
  211. package/dist/updates/redis-presence.d.ts +43 -0
  212. package/dist/updates/redis-presence.d.ts.map +1 -0
  213. package/dist/updates/redis-presence.js +65 -0
  214. package/dist/updates/redis-presence.js.map +1 -0
  215. package/dist/updates/render.d.ts +16 -0
  216. package/dist/updates/render.d.ts.map +1 -0
  217. package/dist/updates/render.js +46 -0
  218. package/dist/updates/render.js.map +1 -0
  219. package/dist/updates/router.d.ts +27 -0
  220. package/dist/updates/router.d.ts.map +1 -0
  221. package/dist/updates/router.js +36 -0
  222. package/dist/updates/router.js.map +1 -0
  223. package/dist/updates/types.d.ts +23 -0
  224. package/dist/updates/types.d.ts.map +1 -0
  225. package/dist/updates/types.js +2 -0
  226. package/dist/updates/types.js.map +1 -0
  227. package/dist/updates/update-bus.d.ts +29 -0
  228. package/dist/updates/update-bus.d.ts.map +1 -0
  229. package/dist/updates/update-bus.js +24 -0
  230. package/dist/updates/update-bus.js.map +1 -0
  231. package/dist/util/bytes.d.ts +12 -0
  232. package/dist/util/bytes.d.ts.map +1 -0
  233. package/dist/util/bytes.js +46 -0
  234. package/dist/util/bytes.js.map +1 -0
  235. package/package.json +84 -0
  236. package/src/auth/handshake.ts +262 -0
  237. package/src/auth/nonce-store.ts +39 -0
  238. package/src/bootstrap.ts +114 -0
  239. package/src/config.ts +103 -0
  240. package/src/core/context.ts +94 -0
  241. package/src/core/errors.ts +52 -0
  242. package/src/core/index.ts +4 -0
  243. package/src/core/rpc.ts +165 -0
  244. package/src/core/updates.ts +69 -0
  245. package/src/create-server.ts +181 -0
  246. package/src/crypto/aes-ige.ts +57 -0
  247. package/src/crypto/dh.ts +101 -0
  248. package/src/crypto/hashes.ts +17 -0
  249. package/src/crypto/msg-key.ts +29 -0
  250. package/src/crypto/rsa.ts +70 -0
  251. package/src/dispatch/dispatcher.ts +586 -0
  252. package/src/dispatch/forwarders/in-process.ts +14 -0
  253. package/src/dispatch/forwarders/print.ts +22 -0
  254. package/src/dispatch/rpc-forwarder.ts +15 -0
  255. package/src/dispatch/types.ts +60 -0
  256. package/src/gateway.ts +214 -0
  257. package/src/index.ts +53 -0
  258. package/src/lib.ts +24 -0
  259. package/src/server/message-pipeline.ts +256 -0
  260. package/src/session/inbound-tracker.ts +221 -0
  261. package/src/session/message-id.ts +43 -0
  262. package/src/session/salts.ts +162 -0
  263. package/src/session/session-manager.ts +66 -0
  264. package/src/storage/index.ts +26 -0
  265. package/src/storage/memory.ts +101 -0
  266. package/src/storage/mongo.ts +215 -0
  267. package/src/storage/types.ts +92 -0
  268. package/src/testkit.ts +19 -0
  269. package/src/tl/codec.ts +292 -0
  270. package/src/tl/layered-registry.ts +132 -0
  271. package/src/tl/protocol.ts +146 -0
  272. package/src/tl/reader.ts +99 -0
  273. package/src/tl/registry.ts +78 -0
  274. package/src/tl/writer.ts +104 -0
  275. package/src/transport/connection-registry.ts +91 -0
  276. package/src/transport/connection.ts +113 -0
  277. package/src/transport/framing.ts +223 -0
  278. package/src/transport/proxy-protocol.ts +109 -0
  279. package/src/transport/server-common.ts +49 -0
  280. package/src/transport/tcp-server.ts +102 -0
  281. package/src/transport/ws-server.ts +94 -0
  282. package/src/update-publisher.ts +47 -0
  283. package/src/updates/mongo-update-log.ts +49 -0
  284. package/src/updates/presence-binder.ts +51 -0
  285. package/src/updates/presence.ts +61 -0
  286. package/src/updates/push.ts +90 -0
  287. package/src/updates/redis-bus.ts +86 -0
  288. package/src/updates/redis-presence.ts +87 -0
  289. package/src/updates/render.ts +53 -0
  290. package/src/updates/router.ts +52 -0
  291. package/src/updates/types.ts +24 -0
  292. package/src/updates/update-bus.ts +49 -0
  293. package/src/util/bytes.ts +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joe Beretta
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.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @mt-tl/server
2
+
3
+ Build an MTProto 2.0 server the way you'd build a [Fastify](https://fastify.dev/)
4
+ app: `createServer`, register routes, `listen`. The framework owns the entire
5
+ protocol — WebSocket + raw-TCP transport, framing, auth-key exchange, sessions,
6
+ server salts, service messages, AES-IGE crypto, TL (de)serialization, layered
7
+ encoding, server-push. You write **methods**.
8
+
9
+ ```bash
10
+ npm install @mt-tl/server
11
+ npm install -D @mt-tl/tl # type generator + codec, used by `gen:types`
12
+ ```
13
+
14
+ ```ts
15
+ import { createServer } from '@mt-tl/server'
16
+ import type { RpcMethods } from './generated/schema.js' // generated from your .tl
17
+
18
+ const app = createServer<RpcMethods>(config)
19
+
20
+ app.method('help.getConfig', { auth: false }, async () => ({ _: 'config' /* … */ }))
21
+
22
+ await app.listen() // opens the WebSocket + raw-TCP carriers
23
+ ```
24
+
25
+ You bring three things: a **config** (`MTProtoConfig`), a **`.tl` schema** (your
26
+ methods), and **handlers**.
27
+
28
+ ## What you get
29
+
30
+ - `createServer` / `definePlugin` — routes and Fastify-style plugins (explicit DI).
31
+ - Hooks (`defineHook`, pre-handlers), `ctx.login/logout/revoke`, `ctx.set/get`.
32
+ - Server-push: `ctx.push(subject, update)`, `ctx.pushToAuthKey(...)`,
33
+ `createUpdatePublisher` (cross-process).
34
+ - Errors that map to `rpc_error`: `BadRequestError`, `AuthRequiredError`,
35
+ `NotFoundError`, `FloodWaitError`, `InternalError`.
36
+ - Optional engine-managed update state (`updates.getState` / `getDifference`) and
37
+ schema-version migrations.
38
+
39
+ In-process by design: the engine and your handlers run in one process; scale by
40
+ running more replicas behind a load balancer (shared state in Mongo + Redis).
41
+
42
+ ## Docs
43
+
44
+ Full guide (getting started, core concepts, sessions & auth, server-push,
45
+ releasing a version, deployment) and a complete copy-me example app live in the
46
+ project repository under `docs/` and `examples/demo-eos-seed-app`.
47
+
48
+ ## License
49
+
50
+ MIT
@@ -0,0 +1,35 @@
1
+ import { type Logger } from '@mt-tl/tl';
2
+ import type { TlCodec } from '../tl/codec.js';
3
+ import type { TlObject } from '@mt-tl/tl';
4
+ import { TlReader } from '../tl/reader.js';
5
+ import type { Storage } from '../storage/index.js';
6
+ import type { SaltService } from '../session/salts.js';
7
+ import { NonceStore } from './nonce-store.js';
8
+ import { type RsaKeyPair } from '../crypto/rsa.js';
9
+ export interface HandshakeDeps {
10
+ codec: TlCodec;
11
+ rsa: RsaKeyPair;
12
+ storage: Storage;
13
+ saltService: SaltService;
14
+ nonceStore: NonceStore;
15
+ defaultLayer: number;
16
+ /** Observability sink; defaults to a no-op logger. */
17
+ logger?: Logger;
18
+ }
19
+ export type HandshakeReply = {
20
+ reply: TlObject;
21
+ } | {
22
+ raw: Buffer;
23
+ } | null;
24
+ export declare class Handshake {
25
+ private readonly deps;
26
+ private readonly logger;
27
+ constructor(deps: HandshakeDeps);
28
+ static isHandshakeId(id: number): boolean;
29
+ /** `reader` is positioned just after the 4-byte constructor id. */
30
+ handle(id: number, reader: TlReader): Promise<HandshakeReply>;
31
+ private handleReqPq;
32
+ private handleReqDhParams;
33
+ private handleSetClientDhParams;
34
+ }
35
+ //# sourceMappingURL=handshake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../src/auth/handshake.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAmBvE,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,EAAE,UAAU,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,WAAW,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAEzE,qBAAa,SAAS;IAGN,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;gBAEF,IAAI,EAAE,aAAa;IAIhD,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IASzC,mEAAmE;IAC7D,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;IAqBnE,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,iBAAiB;YAwDX,uBAAuB;CAmDxC"}
@@ -0,0 +1,208 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { noopLogger } from '@mt-tl/tl';
3
+ import { TlReader } from '../tl/reader.js';
4
+ import { igeEncrypt, igeDecrypt } from '../crypto/aes-ige.js';
5
+ import { sha1, xorBuffers } from '../crypto/hashes.js';
6
+ import { rsaDecryptNoPadding } from '../crypto/rsa.js';
7
+ import { DH_G, DH_PRIME, DH_PRIME_BIGINT, makePQ, modPow, calculatePadding } from '../crypto/dh.js';
8
+ import { toBigIntBE, toBigIntLE, toBufferBE } from '../util/bytes.js';
9
+ // Immutable protocol constructor ids.
10
+ const ID_REQ_PQ = 0x60469778;
11
+ const ID_REQ_PQ_MULTI = 0xbe7e8ef1;
12
+ const ID_REQ_DH_PARAMS = 0xd712e4be;
13
+ const ID_SET_CLIENT_DH_PARAMS = 0xf5045f1f;
14
+ const ID_P_Q_INNER_DATA = 0x83c95aec;
15
+ const ID_P_Q_INNER_DATA_DC = 0xa9f55f95;
16
+ const ID_P_Q_INNER_DATA_TEMP = 0x3c6a84d4;
17
+ const ID_P_Q_INNER_DATA_TEMP_DC = 0x56fddf88;
18
+ const ID_CLIENT_DH_INNER_DATA = 0x6643b654;
19
+ /** Raw -404 (regenerate key) sent when handshake state is missing/invalid. */
20
+ const ERR_404 = Buffer.from('6cfeffff', 'hex');
21
+ export class Handshake {
22
+ deps;
23
+ logger;
24
+ constructor(deps) {
25
+ this.deps = deps;
26
+ this.logger = deps.logger ?? noopLogger;
27
+ }
28
+ static isHandshakeId(id) {
29
+ return (id === ID_REQ_PQ ||
30
+ id === ID_REQ_PQ_MULTI ||
31
+ id === ID_REQ_DH_PARAMS ||
32
+ id === ID_SET_CLIENT_DH_PARAMS);
33
+ }
34
+ /** `reader` is positioned just after the 4-byte constructor id. */
35
+ async handle(id, reader) {
36
+ try {
37
+ switch (id) {
38
+ case ID_REQ_PQ:
39
+ case ID_REQ_PQ_MULTI:
40
+ return this.handleReqPq(reader.readInt128());
41
+ case ID_REQ_DH_PARAMS:
42
+ return this.handleReqDhParams(reader);
43
+ case ID_SET_CLIENT_DH_PARAMS:
44
+ return this.handleSetClientDhParams(reader);
45
+ default:
46
+ return null;
47
+ }
48
+ }
49
+ catch (e) {
50
+ // A malformed/forged handshake step; reply -404 (regenerate key). Client
51
+ // fault, not server fault → warn, with the step id for correlation.
52
+ this.logger.warn('handshake.error', { step: id.toString(16), err: e });
53
+ return { raw: ERR_404 };
54
+ }
55
+ }
56
+ handleReqPq(clientNonce) {
57
+ const serverNonce = randomBytes(16);
58
+ const { p, q, pq } = makePQ();
59
+ this.deps.nonceStore.set(clientNonce.toString('hex'), { clientNonce, serverNonce, p, q, pq });
60
+ const reply = {
61
+ _: 'resPQ',
62
+ nonce: clientNonce,
63
+ server_nonce: serverNonce,
64
+ pq,
65
+ server_public_key_fingerprints: [this.deps.rsa.fingerprint],
66
+ };
67
+ return { reply };
68
+ }
69
+ handleReqDhParams(reader) {
70
+ const nonce = reader.readInt128();
71
+ const serverNonce = reader.readInt128();
72
+ reader.readBytes(); // p
73
+ reader.readBytes(); // q
74
+ reader.readLong(); // public_key_fingerprint
75
+ const encryptedData = reader.readBytes();
76
+ const nd = this.deps.nonceStore.get(nonce.toString('hex'));
77
+ if (!nd)
78
+ return { raw: ERR_404 };
79
+ // RSA decrypt -> [0x00][sha1(20)][ctor id (4)][p_q_inner_data fields][padding]
80
+ const data = rsaDecryptNoPadding(this.deps.rsa.privateKey, encryptedData);
81
+ const inner = readPqInnerData(data.subarray(21));
82
+ if (!inner)
83
+ return { raw: ERR_404 };
84
+ nd.newClientNonce = inner.newNonce;
85
+ nd.expiresIn = inner.expiresIn ?? false;
86
+ // server_DH_inner_data
87
+ const a = randomBelow(DH_PRIME_BIGINT);
88
+ nd.a = a;
89
+ const gA = toBufferBE(modPow(BigInt(DH_G), a, DH_PRIME_BIGINT), 256);
90
+ const innerData = {
91
+ _: 'server_DH_inner_data',
92
+ nonce,
93
+ server_nonce: serverNonce,
94
+ g: DH_G,
95
+ dh_prime: DH_PRIME,
96
+ g_a: gA,
97
+ server_time: Math.floor(Date.now() / 1000),
98
+ };
99
+ const innerBytes = this.deps.codec.encode(innerData);
100
+ const innerHash = sha1(innerBytes);
101
+ const padLen = calculatePadding(innerHash.length + innerBytes.length, 16);
102
+ const plainAnswer = Buffer.concat([
103
+ innerHash,
104
+ innerBytes,
105
+ padLen > 0 ? randomBytes(padLen) : Buffer.alloc(0),
106
+ ]);
107
+ const { tmpAesKey, tmpAesIv } = deriveTmpAes(nd.newClientNonce, serverNonce);
108
+ nd.tmpAesKey = tmpAesKey;
109
+ nd.tmpAesIv = tmpAesIv;
110
+ this.deps.nonceStore.set(nonce.toString('hex'), nd);
111
+ const reply = {
112
+ _: 'server_DH_params_ok',
113
+ nonce,
114
+ server_nonce: serverNonce,
115
+ encrypted_answer: igeEncrypt(plainAnswer, tmpAesKey, tmpAesIv),
116
+ };
117
+ return { reply };
118
+ }
119
+ async handleSetClientDhParams(reader) {
120
+ const nonce = reader.readInt128();
121
+ reader.readInt128(); // server_nonce
122
+ const encryptedData = reader.readBytes();
123
+ const nd = this.deps.nonceStore.get(nonce.toString('hex'));
124
+ if (!nd || !nd.tmpAesKey || !nd.tmpAesIv || !nd.a || !nd.newClientNonce) {
125
+ return { raw: ERR_404 };
126
+ }
127
+ const data = igeDecrypt(encryptedData, nd.tmpAesKey, nd.tmpAesIv);
128
+ // [sha1(20)][ctor id (4)][client_DH_inner_data fields]
129
+ if (data.readUInt32LE(20) !== ID_CLIENT_DH_INNER_DATA)
130
+ return { raw: ERR_404 };
131
+ const gB = readClientDhInner(data.subarray(24));
132
+ const key = toBufferBE(modPow(toBigIntBE(gB), nd.a, DH_PRIME_BIGINT), 256);
133
+ const keyHash = sha1(key);
134
+ const keyId = toBigIntLE(keyHash.subarray(-8));
135
+ // Wire-compat: the FIRST salt keeps its legacy xor(newNonce, serverNonce)
136
+ // derivation; it just becomes window 0 of the rolling schedule.
137
+ const serverSalt = toBigIntLE(xorBuffers(nd.newClientNonce.subarray(0, 8), nd.serverNonce.subarray(0, 8)));
138
+ await this.deps.storage.authKeys.create({
139
+ id: keyId,
140
+ key,
141
+ expiresIn: nd.expiresIn ? true : false,
142
+ createdAt: new Date(),
143
+ subject: null,
144
+ meta: { apiLayer: this.deps.defaultLayer },
145
+ });
146
+ await this.deps.saltService.seed(keyId, serverSalt);
147
+ // A new auth key was negotiated and persisted (an anonymous key until a
148
+ // handler binds a user to it).
149
+ this.logger.info('authkey.create', { authKeyId: keyId, temp: !!nd.expiresIn });
150
+ const newNonceHash1 = sha1(Buffer.concat([nd.newClientNonce, Buffer.from([1]), keyHash.subarray(0, 8)])).subarray(-16);
151
+ this.deps.nonceStore.delete(nonce.toString('hex'));
152
+ const reply = {
153
+ _: 'dh_gen_ok',
154
+ nonce,
155
+ server_nonce: nd.serverNonce,
156
+ new_nonce_hash1: Buffer.from(newNonceHash1),
157
+ };
158
+ return { reply };
159
+ }
160
+ }
161
+ function readPqInnerData(buf) {
162
+ const r = new TlReader(buf);
163
+ const id = r.readUInt32();
164
+ if (id !== ID_P_Q_INNER_DATA &&
165
+ id !== ID_P_Q_INNER_DATA_DC &&
166
+ id !== ID_P_Q_INNER_DATA_TEMP &&
167
+ id !== ID_P_Q_INNER_DATA_TEMP_DC) {
168
+ return null;
169
+ }
170
+ r.readBytes(); // pq
171
+ r.readBytes(); // p
172
+ r.readBytes(); // q
173
+ r.readInt128(); // nonce
174
+ r.readInt128(); // server_nonce
175
+ const newNonce = r.readInt256();
176
+ if (id === ID_P_Q_INNER_DATA_DC || id === ID_P_Q_INNER_DATA_TEMP_DC)
177
+ r.readInt32(); // dc
178
+ let expiresIn;
179
+ if (id === ID_P_Q_INNER_DATA_TEMP || id === ID_P_Q_INNER_DATA_TEMP_DC)
180
+ expiresIn = r.readInt32();
181
+ return { newNonce, expiresIn };
182
+ }
183
+ /** Reads client_DH_inner_data fields (after its ctor id) and returns g_b bytes. */
184
+ function readClientDhInner(buf) {
185
+ const r = new TlReader(buf);
186
+ r.readInt128(); // nonce
187
+ r.readInt128(); // server_nonce
188
+ r.readLong(); // retry_id
189
+ return r.readBytes(); // g_b
190
+ }
191
+ // --- crypto helpers ---------------------------------------------------------
192
+ function deriveTmpAes(newNonce, serverNonce) {
193
+ const nsn = sha1(Buffer.concat([newNonce, serverNonce]));
194
+ const sns = sha1(Buffer.concat([serverNonce, newNonce]));
195
+ const nnn = sha1(Buffer.concat([newNonce, newNonce]));
196
+ return {
197
+ tmpAesKey: Buffer.concat([nsn, sns.subarray(0, 12)]),
198
+ tmpAesIv: Buffer.concat([sns.subarray(12, 20), nnn, newNonce.subarray(0, 4)]),
199
+ };
200
+ }
201
+ function randomBelow(limit) {
202
+ let a;
203
+ do {
204
+ a = toBigIntBE(randomBytes(256));
205
+ } while (a >= limit || a < 2n);
206
+ return a;
207
+ }
208
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/auth/handshake.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /** In-flight auth-key-exchange state, keyed by the client nonce (hex). */
2
+ export interface NonceData {
3
+ clientNonce: Buffer;
4
+ serverNonce: Buffer;
5
+ newClientNonce?: Buffer;
6
+ p?: bigint;
7
+ q?: bigint;
8
+ pq?: Buffer;
9
+ /** server DH secret exponent */
10
+ a?: bigint;
11
+ tmpAesKey?: Buffer;
12
+ tmpAesIv?: Buffer;
13
+ expiresIn?: number | false;
14
+ timer?: NodeJS.Timeout;
15
+ }
16
+ export declare class NonceStore {
17
+ private map;
18
+ set(nonceHex: string, data: NonceData): void;
19
+ get(nonceHex: string): NonceData | undefined;
20
+ delete(nonceHex: string): void;
21
+ }
22
+ //# sourceMappingURL=nonce-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nonce-store.d.ts","sourceRoot":"","sources":["../../src/auth/nonce-store.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,gCAAgC;IAChC,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;CACzB;AAID,qBAAa,UAAU;IACnB,OAAO,CAAC,GAAG,CAA+B;IAE1C,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAQ5C,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI5C,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;CAKjC"}
@@ -0,0 +1,23 @@
1
+ const TTL_MS = 10 * 60 * 1000; // 10 minutes (Telegram drops stale handshakes)
2
+ export class NonceStore {
3
+ map = new Map();
4
+ set(nonceHex, data) {
5
+ const existing = this.map.get(nonceHex);
6
+ if (existing?.timer)
7
+ clearTimeout(existing.timer);
8
+ data.timer = setTimeout(() => this.map.delete(nonceHex), TTL_MS);
9
+ if (typeof data.timer.unref === 'function')
10
+ data.timer.unref();
11
+ this.map.set(nonceHex, data);
12
+ }
13
+ get(nonceHex) {
14
+ return this.map.get(nonceHex);
15
+ }
16
+ delete(nonceHex) {
17
+ const existing = this.map.get(nonceHex);
18
+ if (existing?.timer)
19
+ clearTimeout(existing.timer);
20
+ this.map.delete(nonceHex);
21
+ }
22
+ }
23
+ //# sourceMappingURL=nonce-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nonce-store.js","sourceRoot":"","sources":["../../src/auth/nonce-store.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA,CAAC,+CAA+C;AAE7E,MAAM,OAAO,UAAU;IACX,GAAG,GAAG,IAAI,GAAG,EAAqB,CAAA;IAE1C,GAAG,CAAC,QAAgB,EAAE,IAAe;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACvC,IAAI,QAAQ,EAAE,KAAK;YAAE,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACjD,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAA;QAChE,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU;YAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAC9D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IAChC,CAAC;IAED,GAAG,CAAC,QAAgB;QAChB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACjC,CAAC;IAED,MAAM,CAAC,QAAgB;QACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACvC,IAAI,QAAQ,EAAE,KAAK;YAAE,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACjD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC;CACJ"}
@@ -0,0 +1,36 @@
1
+ import { type Gateway } from './gateway.js';
2
+ import { type UpdateLog } from './core/updates.js';
3
+ import { type Logger } from '@mt-tl/tl';
4
+ import type { MTProtoConfig } from './config.js';
5
+ import type { RpcRequest, RpcResponse } from './dispatch/rpc-forwarder.js';
6
+ import type { UpdateMessage } from './updates/types.js';
7
+ import type { MigrationRegistry } from '@mt-tl/tl';
8
+ /** The app's forward handler — typically `req => dispatchRpc(app.rpc, req, app.deps)`. */
9
+ export type ForwardHandler = (req: RpcRequest) => Promise<RpcResponse>;
10
+ /** Publishes a server update onto the gateway's push loop (no-op when push is off). */
11
+ export type UpdatePublish = (msg: UpdateMessage) => Promise<void>;
12
+ export interface BootstrapOptions {
13
+ config: MTProtoConfig;
14
+ /**
15
+ * Builds the app's forward handler. Receives a `publish` wired to the
16
+ * gateway's in-process push loop and the shared {@link UpdateLog} (durable
17
+ * when `config.updates.managed`) — feed both into the app's update emitter
18
+ * (`new LoggingUpdateEmitter(updateLog, publish)`) so handler-emitted updates
19
+ * reach connected clients and, when managed, persist with a pts.
20
+ */
21
+ createForward: (publish: UpdatePublish, updateLog: UpdateLog) => ForwardHandler;
22
+ logger?: Logger;
23
+ /** Per-predicate migration ladders (input `up` / output `down`). */
24
+ migrations?: MigrationRegistry;
25
+ }
26
+ /**
27
+ * The in-process-first entrypoint: runs the gateway and an app in ONE process.
28
+ * The app is reached via an {@link InProcessForwarder} (no broker). When
29
+ * `config.updates.enabled`, server-push is wired in-process: the app's
30
+ * `publish` → update bus → {@link UpdateRouter} (presence lookup) → this node →
31
+ * client. Uses an in-memory bus/presence for a single process, or Redis (pub/sub
32
+ * bus + presence) when `config.updates.redisUrl` is set (then scale
33
+ * horizontally). Returns the gateway; call `listen()`.
34
+ */
35
+ export declare function bootstrap(opts: BootstrapOptions): Promise<Gateway>;
36
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAQ5E,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAA;AACrE,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAElD,0FAA0F;AAC1F,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;AAEtE,uFAAuF;AACvF,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAEjE,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,aAAa,CAAA;IACrB;;;;;;OAMG;IACH,aAAa,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,KAAK,cAAc,CAAA;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,UAAU,CAAC,EAAE,iBAAiB,CAAA;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAmCxE"}
@@ -0,0 +1,82 @@
1
+ import { buildGateway } from './gateway.js';
2
+ import { InProcessForwarder } from './dispatch/forwarders/in-process.js';
3
+ import { InMemoryUpdateBus } from './updates/update-bus.js';
4
+ import { InMemoryPresence } from './updates/presence.js';
5
+ import { createRedisPresence } from './updates/redis-presence.js';
6
+ import { createRedisUpdateBus } from './updates/redis-bus.js';
7
+ import { createMongoUpdateLog } from './updates/mongo-update-log.js';
8
+ import { UpdateRouter } from './updates/router.js';
9
+ import { InMemoryUpdateLog } from './core/updates.js';
10
+ import { createLogger } from '@mt-tl/tl';
11
+ /**
12
+ * The in-process-first entrypoint: runs the gateway and an app in ONE process.
13
+ * The app is reached via an {@link InProcessForwarder} (no broker). When
14
+ * `config.updates.enabled`, server-push is wired in-process: the app's
15
+ * `publish` → update bus → {@link UpdateRouter} (presence lookup) → this node →
16
+ * client. Uses an in-memory bus/presence for a single process, or Redis (pub/sub
17
+ * bus + presence) when `config.updates.redisUrl` is set (then scale
18
+ * horizontally). Returns the gateway; call `listen()`.
19
+ */
20
+ export async function bootstrap(opts) {
21
+ const logger = opts.logger ?? createLogger({ name: opts.config.nodeId });
22
+ const buildOpts = { logger, migrations: opts.migrations };
23
+ const closers = [];
24
+ let publish = async () => { };
25
+ if (opts.config.updates.enabled) {
26
+ const presence = await makePresence(opts.config);
27
+ const bus = await makeBus(opts.config);
28
+ new UpdateRouter(bus.bus, presence.presence).start();
29
+ publish = msg => bus.bus.publishUpdate(msg);
30
+ buildOpts.presence = presence.presence;
31
+ buildOpts.bus = bus.bus;
32
+ closers.push(bus.close, presence.close);
33
+ logger.info('updates.inprocess', {
34
+ backend: opts.config.updates.redisUrl ? 'redis' : 'memory',
35
+ });
36
+ }
37
+ // Update state (pts log). Durable + engine-answered when `updates.managed`.
38
+ const updateLog = await makeUpdateLog(opts.config);
39
+ closers.push(updateLog.close);
40
+ buildOpts.updateLog = updateLog.log;
41
+ buildOpts.managedUpdates = !!opts.config.updates.managed;
42
+ buildOpts.forwarder = new InProcessForwarder(opts.createForward(publish, updateLog.log));
43
+ const gateway = await buildGateway(opts.config, buildOpts);
44
+ // Extend close() to also tear down the in-process update infra.
45
+ const closeGateway = gateway.close.bind(gateway);
46
+ gateway.close = async () => {
47
+ await closeGateway();
48
+ for (const close of closers)
49
+ await close().catch(() => { });
50
+ };
51
+ return gateway;
52
+ }
53
+ /** In-memory for a single process; Redis once `redisUrl` is set (multi-instance). */
54
+ async function makePresence(config) {
55
+ const u = config.updates;
56
+ if (!u.redisUrl)
57
+ return { presence: new InMemoryPresence(), close: async () => { } };
58
+ return createRedisPresence(u.redisUrl, u.presenceTtlMs);
59
+ }
60
+ async function makeBus(config) {
61
+ const u = config.updates;
62
+ if (!u.redisUrl) {
63
+ const bus = new InMemoryUpdateBus();
64
+ return { bus, close: () => bus.close() };
65
+ }
66
+ return createRedisUpdateBus(u.redisUrl);
67
+ }
68
+ /**
69
+ * The pts log behind `ctx.push` and (when `updates.managed`) `updates.getState`/
70
+ * `getDifference`. Durable on Mongo when managed + `storage.backend: 'mongo'`;
71
+ * in-memory otherwise (the emitter still uses it to stamp a pts).
72
+ */
73
+ async function makeUpdateLog(config) {
74
+ if (config.updates.managed && config.storage.backend === 'mongo') {
75
+ if (!config.storage.mongoUrl || !config.storage.mongoDb) {
76
+ throw new Error('updates.managed with mongo storage requires MONGO_URL and MONGO_DB');
77
+ }
78
+ return createMongoUpdateLog(config.storage.mongoUrl, config.storage.mongoDb);
79
+ }
80
+ return { log: new InMemoryUpdateLog(), close: async () => { } };
81
+ }
82
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import type { StorageBackend } from './storage/index.js';
2
+ /**
3
+ * The configuration object you pass to {@link createServer}. The framework reads
4
+ * **no** environment of its own — your app builds this (its composition root) and
5
+ * hands it in. Only `nodeId`, `defaultLayer`, `schemaDir`, `schemaLayersDir`,
6
+ * `storage`, and `updates` are required.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const config: MTProtoConfig = {
11
+ * nodeId: 'node-1',
12
+ * wsPort: 8081,
13
+ * defaultLayer: 204,
14
+ * schemaDir, // your business .tl
15
+ * schemaLayersDir: layersDir,
16
+ * rsaKeyPath: process.env.RSA_PRIVATE_KEY_PATH,
17
+ * storage: { backend: 'mongo', mongoUrl: process.env.MONGO_URL },
18
+ * updates: { enabled: true, redisUrl: process.env.REDIS_URL, presenceTtlMs: 60_000 },
19
+ * }
20
+ * ```
21
+ */
22
+ export interface MTProtoConfig {
23
+ /** Stable id of this instance, unique per replica — the presence routing key. */
24
+ nodeId: string;
25
+ /** WebSocket listen port. Omit to disable the WS carrier. */
26
+ wsPort?: number;
27
+ /** Raw-TCP listen port. Omit to disable the TCP carrier. */
28
+ tcpPort?: number;
29
+ /** TL layer assumed for a connection until it negotiates one via `invokeWithLayer`. */
30
+ defaultLayer: number;
31
+ /**
32
+ * Whitelist of accepted `initConnection.api_id`s. Omit (default) to accept any
33
+ * id. When set, an `initConnection` carrying an id outside the list is rejected
34
+ * with `rpc_error` `API_ID_INVALID` (400) and its wrapped query is not run —
35
+ * so an unregistered app can't reach your handlers.
36
+ */
37
+ allowedApiIds?: number[];
38
+ /** Directory of your business `.tl` schema (the protocol schema is bundled). */
39
+ schemaDir: string;
40
+ /** Directory of per-layer snapshots (`scheme_N.json`) that drive layered encoding. */
41
+ schemaLayersDir: string;
42
+ /**
43
+ * Path to the server's RSA private key (PEM). Clients pin its fingerprint, so a
44
+ * real client needs the production key here. Omitted → an ephemeral key is
45
+ * generated (handshake works only for test clients).
46
+ */
47
+ rsaKeyPath?: string;
48
+ /**
49
+ * Disable the inbound MTProto 2.0 `msg_key` integrity check. ⚠️ INSECURE — keep
50
+ * `false` (the default). Only enable as a temporary interop shim for a
51
+ * non-compliant client. See docs/internals/msgkey-v1-quirk.md.
52
+ */
53
+ disableMsgKeyCheck?: boolean;
54
+ /**
55
+ * Disable inbound sequence-number validation (`bad_msg_notification` codes
56
+ * 32/34/35). Default `false` = enforced: content-related messages (RPC queries)
57
+ * must carry an odd, strictly increasing `seqno` and pure service messages an
58
+ * even one. Set `true` as an interop shim for a client that does not set `seqno`
59
+ * to spec — the same escape-hatch pattern as {@link disableMsgKeyCheck}.
60
+ */
61
+ disableSeqNoCheck?: boolean;
62
+ /**
63
+ * Trust an upstream proxy/load balancer for the client address: parse the
64
+ * PROXY-protocol header (v1/v2) on the raw-TCP carrier, and trust
65
+ * `X-Forwarded-For` on WebSocket. Default `false` — leave off when clients
66
+ * connect directly, since both are spoofable by a direct client. When `true`,
67
+ * the announced IP surfaces as `ctx.request.ip`.
68
+ */
69
+ trustProxy?: boolean;
70
+ /** Where auth keys, server salts, and sessions persist. */
71
+ storage: {
72
+ /** `'memory'` (single process, dev) or `'mongo'` (shared, multi-replica). */
73
+ backend: StorageBackend;
74
+ /** Mongo connection string (required when `backend: 'mongo'`). */
75
+ mongoUrl?: string;
76
+ /** Mongo database name (defaults to the driver's database in the URL). */
77
+ mongoDb?: string;
78
+ };
79
+ /** Server-push (updates) delivery. */
80
+ updates: {
81
+ /** Master switch for server-push. When `false`, `ctx.push` is a no-op. */
82
+ enabled: boolean;
83
+ /**
84
+ * Redis URL for cross-instance presence + the pub/sub update bus. Omit for a
85
+ * single process (in-memory presence/bus); required to deliver push across
86
+ * replicas.
87
+ */
88
+ redisUrl?: string;
89
+ /** Presence entry TTL in ms; the node refreshes it on a heartbeat. */
90
+ presenceTtlMs: number;
91
+ /**
92
+ * Who owns the update state (`pts` + `updates.getState`/`getDifference`).
93
+ * `false` (default) → your app owns it: handle those methods yourself and
94
+ * embed `pts` in the updates you push. `true` → the engine owns it: it keeps
95
+ * a durable per-user pts log (Mongo when `storage.backend: 'mongo'`, else
96
+ * in-memory) and answers `updates.getState`/`updates.getDifference` itself.
97
+ * Requires the `updates.*` types in your schema. Common-pts only — no qts,
98
+ * seq, or per-channel pts.
99
+ */
100
+ managed?: boolean;
101
+ };
102
+ }
103
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,aAAa;IAC1B,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uFAAuF;IACvF,YAAY,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAA;IACjB,sFAAsF;IACtF,eAAe,EAAE,MAAM,CAAA;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2DAA2D;IAC3D,OAAO,EAAE;QACL,6EAA6E;QAC7E,OAAO,EAAE,cAAc,CAAA;QACvB,kEAAkE;QAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,0EAA0E;QAC1E,OAAO,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;IACD,sCAAsC;IACtC,OAAO,EAAE;QACL,0EAA0E;QAC1E,OAAO,EAAE,OAAO,CAAA;QAChB;;;;WAIG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,sEAAsE;QACtE,aAAa,EAAE,MAAM,CAAA;QACrB;;;;;;;;WAQG;QACH,OAAO,CAAC,EAAE,OAAO,CAAA;KACpB,CAAA;CACJ"}
package/dist/config.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":""}