@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.
- package/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/auth/handshake.d.ts +35 -0
- package/dist/auth/handshake.d.ts.map +1 -0
- package/dist/auth/handshake.js +208 -0
- package/dist/auth/handshake.js.map +1 -0
- package/dist/auth/nonce-store.d.ts +22 -0
- package/dist/auth/nonce-store.d.ts.map +1 -0
- package/dist/auth/nonce-store.js +23 -0
- package/dist/auth/nonce-store.js.map +1 -0
- package/dist/bootstrap.d.ts +36 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +82 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/config.d.ts +103 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -0
- package/dist/core/context.d.ts +57 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +27 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/errors.d.ts +33 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +47 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/rpc.d.ts +83 -0
- package/dist/core/rpc.d.ts.map +1 -0
- package/dist/core/rpc.js +102 -0
- package/dist/core/rpc.js.map +1 -0
- package/dist/core/updates.d.ts +56 -0
- package/dist/core/updates.d.ts.map +1 -0
- package/dist/core/updates.js +34 -0
- package/dist/core/updates.js.map +1 -0
- package/dist/create-server.d.ts +89 -0
- package/dist/create-server.d.ts.map +1 -0
- package/dist/create-server.js +109 -0
- package/dist/create-server.js.map +1 -0
- package/dist/crypto/aes-ige.d.ts +3 -0
- package/dist/crypto/aes-ige.d.ts.map +1 -0
- package/dist/crypto/aes-ige.js +55 -0
- package/dist/crypto/aes-ige.js.map +1 -0
- package/dist/crypto/dh.d.ts +21 -0
- package/dist/crypto/dh.d.ts.map +1 -0
- package/dist/crypto/dh.js +99 -0
- package/dist/crypto/dh.js.map +1 -0
- package/dist/crypto/hashes.d.ts +6 -0
- package/dist/crypto/hashes.d.ts.map +1 -0
- package/dist/crypto/hashes.js +14 -0
- package/dist/crypto/hashes.js.map +1 -0
- package/dist/crypto/msg-key.d.ts +15 -0
- package/dist/crypto/msg-key.d.ts.map +1 -0
- package/dist/crypto/msg-key.js +24 -0
- package/dist/crypto/msg-key.js.map +1 -0
- package/dist/crypto/rsa.d.ts +27 -0
- package/dist/crypto/rsa.d.ts.map +1 -0
- package/dist/crypto/rsa.js +50 -0
- package/dist/crypto/rsa.js.map +1 -0
- package/dist/dispatch/dispatcher.d.ts +72 -0
- package/dist/dispatch/dispatcher.d.ts.map +1 -0
- package/dist/dispatch/dispatcher.js +503 -0
- package/dist/dispatch/dispatcher.js.map +1 -0
- package/dist/dispatch/forwarders/in-process.d.ts +12 -0
- package/dist/dispatch/forwarders/in-process.d.ts.map +1 -0
- package/dist/dispatch/forwarders/in-process.js +15 -0
- package/dist/dispatch/forwarders/in-process.js.map +1 -0
- package/dist/dispatch/forwarders/print.d.ts +14 -0
- package/dist/dispatch/forwarders/print.d.ts.map +1 -0
- package/dist/dispatch/forwarders/print.js +23 -0
- package/dist/dispatch/forwarders/print.js.map +1 -0
- package/dist/dispatch/rpc-forwarder.d.ts +14 -0
- package/dist/dispatch/rpc-forwarder.d.ts.map +1 -0
- package/dist/dispatch/rpc-forwarder.js +2 -0
- package/dist/dispatch/rpc-forwarder.js.map +1 -0
- package/dist/dispatch/types.d.ts +32 -0
- package/dist/dispatch/types.d.ts.map +1 -0
- package/dist/dispatch/types.js +23 -0
- package/dist/dispatch/types.js.map +1 -0
- package/dist/gateway.d.ts +57 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +150 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +12 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +13 -0
- package/dist/lib.js.map +1 -0
- package/dist/server/message-pipeline.d.ts +57 -0
- package/dist/server/message-pipeline.d.ts.map +1 -0
- package/dist/server/message-pipeline.js +199 -0
- package/dist/server/message-pipeline.js.map +1 -0
- package/dist/session/inbound-tracker.d.ts +118 -0
- package/dist/session/inbound-tracker.d.ts.map +1 -0
- package/dist/session/inbound-tracker.js +170 -0
- package/dist/session/inbound-tracker.js.map +1 -0
- package/dist/session/message-id.d.ts +19 -0
- package/dist/session/message-id.d.ts.map +1 -0
- package/dist/session/message-id.js +30 -0
- package/dist/session/message-id.js.map +1 -0
- package/dist/session/salts.d.ts +51 -0
- package/dist/session/salts.d.ts.map +1 -0
- package/dist/session/salts.js +132 -0
- package/dist/session/salts.js.map +1 -0
- package/dist/session/session-manager.d.ts +18 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +43 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/storage/index.d.ts +14 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +16 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/memory.d.ts +3 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +91 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/mongo.d.ts +3 -0
- package/dist/storage/mongo.d.ts.map +1 -0
- package/dist/storage/mongo.js +175 -0
- package/dist/storage/mongo.js.map +1 -0
- package/dist/storage/types.d.ts +85 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +3 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/testkit.d.ts +11 -0
- package/dist/testkit.d.ts.map +1 -0
- package/dist/testkit.js +17 -0
- package/dist/testkit.js.map +1 -0
- package/dist/tl/codec.d.ts +37 -0
- package/dist/tl/codec.d.ts.map +1 -0
- package/dist/tl/codec.js +297 -0
- package/dist/tl/codec.js.map +1 -0
- package/dist/tl/layered-registry.d.ts +29 -0
- package/dist/tl/layered-registry.d.ts.map +1 -0
- package/dist/tl/layered-registry.js +118 -0
- package/dist/tl/layered-registry.js.map +1 -0
- package/dist/tl/protocol.d.ts +126 -0
- package/dist/tl/protocol.d.ts.map +1 -0
- package/dist/tl/protocol.js +22 -0
- package/dist/tl/protocol.js.map +1 -0
- package/dist/tl/reader.d.ts +30 -0
- package/dist/tl/reader.d.ts.map +1 -0
- package/dist/tl/reader.js +87 -0
- package/dist/tl/reader.js.map +1 -0
- package/dist/tl/registry.d.ts +39 -0
- package/dist/tl/registry.d.ts.map +1 -0
- package/dist/tl/registry.js +52 -0
- package/dist/tl/registry.js.map +1 -0
- package/dist/tl/writer.d.ts +24 -0
- package/dist/tl/writer.d.ts.map +1 -0
- package/dist/tl/writer.js +95 -0
- package/dist/tl/writer.js.map +1 -0
- package/dist/transport/connection-registry.d.ts +31 -0
- package/dist/transport/connection-registry.d.ts.map +1 -0
- package/dist/transport/connection-registry.js +84 -0
- package/dist/transport/connection-registry.js.map +1 -0
- package/dist/transport/connection.d.ts +62 -0
- package/dist/transport/connection.d.ts.map +1 -0
- package/dist/transport/connection.js +77 -0
- package/dist/transport/connection.js.map +1 -0
- package/dist/transport/framing.d.ts +39 -0
- package/dist/transport/framing.d.ts.map +1 -0
- package/dist/transport/framing.js +212 -0
- package/dist/transport/framing.js.map +1 -0
- package/dist/transport/proxy-protocol.d.ts +23 -0
- package/dist/transport/proxy-protocol.d.ts.map +1 -0
- package/dist/transport/proxy-protocol.js +108 -0
- package/dist/transport/proxy-protocol.js.map +1 -0
- package/dist/transport/server-common.d.ts +27 -0
- package/dist/transport/server-common.d.ts.map +1 -0
- package/dist/transport/server-common.js +27 -0
- package/dist/transport/server-common.js.map +1 -0
- package/dist/transport/tcp-server.d.ts +26 -0
- package/dist/transport/tcp-server.d.ts.map +1 -0
- package/dist/transport/tcp-server.js +91 -0
- package/dist/transport/tcp-server.js.map +1 -0
- package/dist/transport/ws-server.d.ts +19 -0
- package/dist/transport/ws-server.d.ts.map +1 -0
- package/dist/transport/ws-server.js +78 -0
- package/dist/transport/ws-server.js.map +1 -0
- package/dist/update-publisher.d.ts +34 -0
- package/dist/update-publisher.d.ts.map +1 -0
- package/dist/update-publisher.js +29 -0
- package/dist/update-publisher.js.map +1 -0
- package/dist/updates/mongo-update-log.d.ts +13 -0
- package/dist/updates/mongo-update-log.d.ts.map +1 -0
- package/dist/updates/mongo-update-log.js +39 -0
- package/dist/updates/mongo-update-log.js.map +1 -0
- package/dist/updates/presence-binder.d.ts +29 -0
- package/dist/updates/presence-binder.d.ts.map +1 -0
- package/dist/updates/presence-binder.js +36 -0
- package/dist/updates/presence-binder.js.map +1 -0
- package/dist/updates/presence.d.ts +31 -0
- package/dist/updates/presence.d.ts.map +1 -0
- package/dist/updates/presence.js +44 -0
- package/dist/updates/presence.js.map +1 -0
- package/dist/updates/push.d.ts +25 -0
- package/dist/updates/push.d.ts.map +1 -0
- package/dist/updates/push.js +72 -0
- package/dist/updates/push.js.map +1 -0
- package/dist/updates/redis-bus.d.ts +45 -0
- package/dist/updates/redis-bus.d.ts.map +1 -0
- package/dist/updates/redis-bus.js +59 -0
- package/dist/updates/redis-bus.js.map +1 -0
- package/dist/updates/redis-presence.d.ts +43 -0
- package/dist/updates/redis-presence.d.ts.map +1 -0
- package/dist/updates/redis-presence.js +65 -0
- package/dist/updates/redis-presence.js.map +1 -0
- package/dist/updates/render.d.ts +16 -0
- package/dist/updates/render.d.ts.map +1 -0
- package/dist/updates/render.js +46 -0
- package/dist/updates/render.js.map +1 -0
- package/dist/updates/router.d.ts +27 -0
- package/dist/updates/router.d.ts.map +1 -0
- package/dist/updates/router.js +36 -0
- package/dist/updates/router.js.map +1 -0
- package/dist/updates/types.d.ts +23 -0
- package/dist/updates/types.d.ts.map +1 -0
- package/dist/updates/types.js +2 -0
- package/dist/updates/types.js.map +1 -0
- package/dist/updates/update-bus.d.ts +29 -0
- package/dist/updates/update-bus.d.ts.map +1 -0
- package/dist/updates/update-bus.js +24 -0
- package/dist/updates/update-bus.js.map +1 -0
- package/dist/util/bytes.d.ts +12 -0
- package/dist/util/bytes.d.ts.map +1 -0
- package/dist/util/bytes.js +46 -0
- package/dist/util/bytes.js.map +1 -0
- package/package.json +84 -0
- package/src/auth/handshake.ts +262 -0
- package/src/auth/nonce-store.ts +39 -0
- package/src/bootstrap.ts +114 -0
- package/src/config.ts +103 -0
- package/src/core/context.ts +94 -0
- package/src/core/errors.ts +52 -0
- package/src/core/index.ts +4 -0
- package/src/core/rpc.ts +165 -0
- package/src/core/updates.ts +69 -0
- package/src/create-server.ts +181 -0
- package/src/crypto/aes-ige.ts +57 -0
- package/src/crypto/dh.ts +101 -0
- package/src/crypto/hashes.ts +17 -0
- package/src/crypto/msg-key.ts +29 -0
- package/src/crypto/rsa.ts +70 -0
- package/src/dispatch/dispatcher.ts +586 -0
- package/src/dispatch/forwarders/in-process.ts +14 -0
- package/src/dispatch/forwarders/print.ts +22 -0
- package/src/dispatch/rpc-forwarder.ts +15 -0
- package/src/dispatch/types.ts +60 -0
- package/src/gateway.ts +214 -0
- package/src/index.ts +53 -0
- package/src/lib.ts +24 -0
- package/src/server/message-pipeline.ts +256 -0
- package/src/session/inbound-tracker.ts +221 -0
- package/src/session/message-id.ts +43 -0
- package/src/session/salts.ts +162 -0
- package/src/session/session-manager.ts +66 -0
- package/src/storage/index.ts +26 -0
- package/src/storage/memory.ts +101 -0
- package/src/storage/mongo.ts +215 -0
- package/src/storage/types.ts +92 -0
- package/src/testkit.ts +19 -0
- package/src/tl/codec.ts +292 -0
- package/src/tl/layered-registry.ts +132 -0
- package/src/tl/protocol.ts +146 -0
- package/src/tl/reader.ts +99 -0
- package/src/tl/registry.ts +78 -0
- package/src/tl/writer.ts +104 -0
- package/src/transport/connection-registry.ts +91 -0
- package/src/transport/connection.ts +113 -0
- package/src/transport/framing.ts +223 -0
- package/src/transport/proxy-protocol.ts +109 -0
- package/src/transport/server-common.ts +49 -0
- package/src/transport/tcp-server.ts +102 -0
- package/src/transport/ws-server.ts +94 -0
- package/src/update-publisher.ts +47 -0
- package/src/updates/mongo-update-log.ts +49 -0
- package/src/updates/presence-binder.ts +51 -0
- package/src/updates/presence.ts +61 -0
- package/src/updates/push.ts +90 -0
- package/src/updates/redis-bus.ts +86 -0
- package/src/updates/redis-presence.ts +87 -0
- package/src/updates/render.ts +53 -0
- package/src/updates/router.ts +52 -0
- package/src/updates/types.ts +24 -0
- package/src/updates/update-bus.ts +49 -0
- 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":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,UAAU,EAAe,MAAM,WAAW,CAAA;AAGnD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAI1C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAmB,MAAM,kBAAkB,CAAA;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACnG,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAErE,sCAAsC;AACtC,MAAM,SAAS,GAAG,UAAU,CAAA;AAC5B,MAAM,eAAe,GAAG,UAAU,CAAA;AAClC,MAAM,gBAAgB,GAAG,UAAU,CAAA;AACnC,MAAM,uBAAuB,GAAG,UAAU,CAAA;AAE1C,MAAM,iBAAiB,GAAG,UAAU,CAAA;AACpC,MAAM,oBAAoB,GAAG,UAAU,CAAA;AACvC,MAAM,sBAAsB,GAAG,UAAU,CAAA;AACzC,MAAM,yBAAyB,GAAG,UAAU,CAAA;AAC5C,MAAM,uBAAuB,GAAG,UAAU,CAAA;AAE1C,8EAA8E;AAC9E,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAA;AAe9C,MAAM,OAAO,SAAS;IAGW;IAFZ,MAAM,CAAQ;IAE/B,YAA6B,IAAmB;QAAnB,SAAI,GAAJ,IAAI,CAAe;QAC5C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,UAAU,CAAA;IAC3C,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,EAAU;QAC3B,OAAO,CACH,EAAE,KAAK,SAAS;YAChB,EAAE,KAAK,eAAe;YACtB,EAAE,KAAK,gBAAgB;YACvB,EAAE,KAAK,uBAAuB,CACjC,CAAA;IACL,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,MAAgB;QACrC,IAAI,CAAC;YACD,QAAQ,EAAE,EAAE,CAAC;gBACT,KAAK,SAAS,CAAC;gBACf,KAAK,eAAe;oBAChB,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;gBAChD,KAAK,gBAAgB;oBACjB,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAA;gBACzC,KAAK,uBAAuB;oBACxB,OAAO,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAA;gBAC/C;oBACI,OAAO,IAAI,CAAA;YACnB,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,yEAAyE;YACzE,oEAAoE;YACpE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;YACtE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAA;QAC3B,CAAC;IACL,CAAC;IAEO,WAAW,CAAC,WAAmB;QACnC,MAAM,WAAW,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;QACnC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,CAAA;QAC7B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAE7F,MAAM,KAAK,GAAa;YACpB,CAAC,EAAE,OAAO;YACV,KAAK,EAAE,WAAW;YAClB,YAAY,EAAE,WAAW;YACzB,EAAE;YACF,8BAA8B,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;SAC9D,CAAA;QACD,OAAO,EAAE,KAAK,EAAE,CAAA;IACpB,CAAC;IAEO,iBAAiB,CAAC,MAAgB;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAA;QACjC,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,EAAE,CAAA;QACvC,MAAM,CAAC,SAAS,EAAE,CAAA,CAAC,IAAI;QACvB,MAAM,CAAC,SAAS,EAAE,CAAA,CAAC,IAAI;QACvB,MAAM,CAAC,QAAQ,EAAE,CAAA,CAAC,yBAAyB;QAC3C,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,EAAE,CAAA;QAExC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;QAC1D,IAAI,CAAC,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAA;QAEhC,+EAA+E;QAC/E,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,CAAA;QACzE,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;QAChD,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAA;QAEnC,EAAE,CAAC,cAAc,GAAG,KAAK,CAAC,QAAQ,CAAA;QAClC,EAAE,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAA;QAEvC,uBAAuB;QACvB,MAAM,CAAC,GAAG,WAAW,CAAC,eAAe,CAAC,CAAA;QACtC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;QACR,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,CAAA;QAEpE,MAAM,SAAS,GAAa;YACxB,CAAC,EAAE,sBAAsB;YACzB,KAAK;YACL,YAAY,EAAE,WAAW;YACzB,CAAC,EAAE,IAAI;YACP,QAAQ,EAAE,QAAQ;YAClB,GAAG,EAAE,EAAE;YACP,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;SAC7C,CAAA;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,CAAA;QAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QACzE,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;YAC9B,SAAS;YACT,UAAU;YACV,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;SACrD,CAAC,CAAA;QAEF,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,EAAE,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QAC5E,EAAE,CAAC,SAAS,GAAG,SAAS,CAAA;QACxB,EAAE,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACtB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAA;QAEnD,MAAM,KAAK,GAAa;YACpB,CAAC,EAAE,qBAAqB;YACxB,KAAK;YACL,YAAY,EAAE,WAAW;YACzB,gBAAgB,EAAE,UAAU,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC;SACjE,CAAA;QACD,OAAO,EAAE,KAAK,EAAE,CAAA;IACpB,CAAC;IAEO,KAAK,CAAC,uBAAuB,CAAC,MAAgB;QAClD,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAA;QACjC,MAAM,CAAC,UAAU,EAAE,CAAA,CAAC,eAAe;QACnC,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,EAAE,CAAA;QAExC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;QAC1D,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC;YACtE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAA;QAC3B,CAAC;QAED,MAAM,IAAI,GAAG,UAAU,CAAC,aAAa,EAAE,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAA;QACjE,uDAAuD;QACvD,IAAI,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,uBAAuB;YAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAA;QAC9E,MAAM,EAAE,GAAG,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;QAE/C,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,CAAA;QAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;QACzB,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9C,0EAA0E;QAC1E,gEAAgE;QAChE,MAAM,UAAU,GAAG,UAAU,CACzB,UAAU,CAAC,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAC9E,CAAA;QAED,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;YACpC,EAAE,EAAE,KAAK;YACT,GAAG;YACH,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK;YACtC,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE;SAC7C,CAAC,CAAA;QACF,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;QACnD,wEAAwE;QACxE,+BAA+B;QAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAA;QAE9E,MAAM,aAAa,GAAG,IAAI,CACtB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAC/E,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAA;QAEf,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;QAElD,MAAM,KAAK,GAAa;YACpB,CAAC,EAAE,WAAW;YACd,KAAK;YACL,YAAY,EAAE,EAAE,CAAC,WAAW;YAC5B,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;SAC9C,CAAA;QACD,OAAO,EAAE,KAAK,EAAE,CAAA;IACpB,CAAC;CACJ;AASD,SAAS,eAAe,CAAC,GAAW;IAChC,MAAM,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC3B,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,EAAE,CAAA;IACzB,IACI,EAAE,KAAK,iBAAiB;QACxB,EAAE,KAAK,oBAAoB;QAC3B,EAAE,KAAK,sBAAsB;QAC7B,EAAE,KAAK,yBAAyB,EAClC,CAAC;QACC,OAAO,IAAI,CAAA;IACf,CAAC;IACD,CAAC,CAAC,SAAS,EAAE,CAAA,CAAC,KAAK;IACnB,CAAC,CAAC,SAAS,EAAE,CAAA,CAAC,IAAI;IAClB,CAAC,CAAC,SAAS,EAAE,CAAA,CAAC,IAAI;IAClB,CAAC,CAAC,UAAU,EAAE,CAAA,CAAC,QAAQ;IACvB,CAAC,CAAC,UAAU,EAAE,CAAA,CAAC,eAAe;IAC9B,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,EAAE,CAAA;IAC/B,IAAI,EAAE,KAAK,oBAAoB,IAAI,EAAE,KAAK,yBAAyB;QAAE,CAAC,CAAC,SAAS,EAAE,CAAA,CAAC,KAAK;IACxF,IAAI,SAA6B,CAAA;IACjC,IAAI,EAAE,KAAK,sBAAsB,IAAI,EAAE,KAAK,yBAAyB;QAAE,SAAS,GAAG,CAAC,CAAC,SAAS,EAAE,CAAA;IAChG,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAA;AAClC,CAAC;AAED,mFAAmF;AACnF,SAAS,iBAAiB,CAAC,GAAW;IAClC,MAAM,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC3B,CAAC,CAAC,UAAU,EAAE,CAAA,CAAC,QAAQ;IACvB,CAAC,CAAC,UAAU,EAAE,CAAA,CAAC,eAAe;IAC9B,CAAC,CAAC,QAAQ,EAAE,CAAA,CAAC,WAAW;IACxB,OAAO,CAAC,CAAC,SAAS,EAAE,CAAA,CAAC,MAAM;AAC/B,CAAC;AAED,+EAA+E;AAE/E,SAAS,YAAY,CAAC,QAAgB,EAAE,WAAmB;IACvD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IACrD,OAAO;QACH,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACpD,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;KAChF,CAAA;AACL,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAC9B,IAAI,CAAS,CAAA;IACb,GAAG,CAAC;QACA,CAAC,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;IACpC,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,EAAE,EAAC;IAC9B,OAAO,CAAC,CAAA;AACZ,CAAC"}
|
|
@@ -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":"AAAA,OAAO,EAAE,YAAY,EAAmC,MAAM,cAAc,CAAA;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAA;AACxE,OAAO,EAAE,iBAAiB,EAAkB,MAAM,yBAAyB,CAAA;AAC3E,OAAO,EAAE,gBAAgB,EAAiB,MAAM,uBAAuB,CAAA;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAkB,MAAM,mBAAmB,CAAA;AACrE,OAAO,EAAE,YAAY,EAAe,MAAM,WAAW,CAAA;AA2BrD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAsB;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IACxE,MAAM,SAAS,GAAiB,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAA;IACvE,MAAM,OAAO,GAA+B,EAAE,CAAA;IAC9C,IAAI,OAAO,GAAkB,KAAK,IAAI,EAAE,GAAE,CAAC,CAAA;IAE3C,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAChD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACtC,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAA;QACpD,OAAO,GAAG,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA;QAC3C,SAAS,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAA;QACtC,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAA;QACvB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAA;QACvC,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;YAC7B,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ;SAC7D,CAAC,CAAA;IACN,CAAC;IAED,4EAA4E;IAC5E,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAClD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC7B,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,CAAA;IACnC,SAAS,CAAC,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAA;IAExD,SAAS,CAAC,SAAS,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IACxF,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAE1D,gEAAgE;IAChE,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAChD,OAAO,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE;QACvB,MAAM,YAAY,EAAE,CAAA;QACpB,KAAK,MAAM,KAAK,IAAI,OAAO;YAAE,MAAM,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAC9D,CAAC,CAAA;IACD,OAAO,OAAO,CAAA;AAClB,CAAC;AAED,qFAAqF;AACrF,KAAK,UAAU,YAAY,CACvB,MAAqB;IAErB,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAA;IACxB,IAAI,CAAC,CAAC,CAAC,QAAQ;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,gBAAgB,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,CAAA;IACnF,OAAO,mBAAmB,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,aAAa,CAAC,CAAA;AAC3D,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,MAAqB;IACxC,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAA;IACxB,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,GAAG,GAAG,IAAI,iBAAiB,EAAE,CAAA;QACnC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAA;IAC5C,CAAC;IACD,OAAO,oBAAoB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;AAC3C,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAAC,MAAqB;IAC9C,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAC/D,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;QACzF,CAAC;QACD,OAAO,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAChF,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,IAAI,iBAAiB,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,CAAA;AAClE,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":""}
|