Skip to content

Commit eadc2c7

Browse files
author
qwe7002
committed
feat: add Config Transfer API documentation and update project instructions
- Added a new entry for "Config Transfer API" in the VitePress configuration. - Updated project instructions to provide a comprehensive overview of the Telegram SMS app, including features, technology stack, architecture, and key dependencies. - Introduced a new document detailing the Config Transfer API, explaining its purpose, tech stack, endpoints, and how it integrates with the Android app and web Config Generator.
1 parent 004b6dd commit eadc2c7

3 files changed

Lines changed: 359 additions & 497 deletions

File tree

.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export default defineConfigWithTheme<ExtendedConfig>({
3636
{ text: "Crypto Module Documentation", link: "/CRYPTO_DOC" },
3737
{ text: "Data Structure Version Management", link: "/DATA_STRUCTURE_VERSION" },
3838
{ text: "Self-hosted Bot API", link: "/self_hosted_bot_api" },
39+
{ text: "Config Transfer API", link: "/config_transfer_api" },
3940
{ text: "Carbon Copy Provider Implementation", link: "/CarbonCopyProvider" },
4041
{ text: "String Resources Organization", link: "/STRING_RESOURCES" },
4142
{ text: "Update Check System", link: "/UPDATE_CHECK" },

docs/config_transfer_api.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Config Transfer API
2+
3+
Telegram SMS uses a small Cloudflare Worker (`cf-kv-storage`) as a **short-lived, encrypted config-transfer relay**. It lets the web [Config Generator](https://config.telegram-sms.com/) and the Android app's *Transfer config* feature (`TransferConfigActivity`) exchange configuration without a persistent backend or user accounts.
4+
5+
The relay never sees plaintext. The client encrypts the configuration locally and uploads only the opaque ciphertext; the server stores it under a short, human-shareable key and returns it exactly once. Security comes from three layers working together:
6+
7+
1. **Client-side encryption** — the payload is encrypted before upload (libsodium SecretBox, the same `Crypto` scheme the app uses elsewhere).
8+
2. **Short TTL** — config entries expire after **1 hour**.
9+
3. **One-time read** — fetching a config deletes it immediately, so a key can only be redeemed once.
10+
11+
The key itself is intentionally short and is *not* a secret; it is meant to be typed or scanned between two of your own devices within a few minutes.
12+
13+
## Tech stack
14+
15+
- **Cloudflare Workers** — serverless runtime
16+
- **itty-router** (`AutoRouter`) — routing + CORS (`preflight` / `corsify`)
17+
- **Workers KV** — storage for the encrypted blobs
18+
- **TypeScript**, tested with **Vitest** (`@cloudflare/vitest-pool-workers`), deployed with **Wrangler**
19+
20+
The entire service is two source files: `src/index.ts` (routes) and `src/snowflake.ts` (key generator).
21+
22+
## Endpoints
23+
24+
| Method & path | KV namespace | TTL | Read behaviour | Used by |
25+
| -------------------- | ----------------- | ------ | --------------------- | -------------------------------- |
26+
| `GET /` ||| 302 → telegram-sms.com ||
27+
| `PUT /config` | `telegram_config` | 1 hour || Upload main app config |
28+
| `GET /config?key=` | `telegram_config` || one-time (deletes) | Download main app config |
29+
| `PUT /cc-config` | `cc_config` | 1 hour || Upload Carbon Copy config |
30+
| `GET /cc-config?key=`| `cc_config` || one-time (deletes) | Download Carbon Copy config |
31+
| `POST /log` | `telegram_log` | none | no read route | Upload diagnostic log (write-only) |
32+
33+
### Upload (`PUT /config`, `PUT /cc-config`)
34+
35+
Body is a JSON object whose `encrypt` field holds the already-encrypted blob. The Worker generates a key, stores the blob with a 1-hour TTL, and returns the key.
36+
37+
```bash
38+
curl -X PUT https://<worker-host>/config \
39+
-H "Content-Type: application/json" \
40+
-d '{"encrypt": "<client-encrypted-config>"}'
41+
# → {"key":"abc123xyz"}
42+
```
43+
44+
### Download (`GET /config`, `GET /cc-config`)
45+
46+
Pass the key as the `key` query parameter. The Worker returns the stored blob **and deletes it**, so a second request with the same key returns `404 Value not found`.
47+
48+
```bash
49+
curl "https://<worker-host>/config?key=abc123xyz"
50+
# → <client-encrypted-config> (then the key is gone)
51+
```
52+
53+
### Diagnostic log (`POST /log`)
54+
55+
Stores an encrypted blob in `telegram_log` and returns a key. There is intentionally **no read route** and **no TTL** on this namespace — it is a write-only sink for diagnostics inspected out-of-band.
56+
57+
## Key generation
58+
59+
Keys come from `Snowflake.generateKey()` (`src/snowflake.ts`), a singleton that builds a base-36 string from a **21-bit timestamp + 13-bit sequence** (Snowflake-style), right-padded with random `[A-Za-z0-9]` characters to a fixed **9 characters**. The result is short enough to read aloud or scan, which is the whole point — it is a transient handle, not a credential.
60+
61+
## CORS
62+
63+
`cors()` is configured in `src/index.ts`:
64+
65+
- **Allowed origins:** `http://localhost:5173` (local Config Generator dev) and `https://config.telegram-sms.com`
66+
- **Allowed methods:** `GET`, `PUT`
67+
- **Allowed headers:** `Content-Type`
68+
69+
> Because `allowMethods` lists only `GET` and `PUT`, the `POST /log` route is **not** reachable cross-origin from the config site under the current configuration.
70+
71+
## KV bindings
72+
73+
`wrangler.toml` declares two namespaces:
74+
75+
```toml
76+
[[kv_namespaces]]
77+
binding = "telegram_config"
78+
id = "..."
79+
80+
[[kv_namespaces]]
81+
binding = "cc_config"
82+
id = "..."
83+
```
84+
85+
> **Known gap:** `telegram_log` is referenced by `POST /log` but is **not** declared in `wrangler.toml`. The `/log` route will fail until a `telegram_log` binding is added.
86+
87+
## Develop, test, deploy
88+
89+
```bash
90+
npm run dev # wrangler dev — local server
91+
npm test # vitest
92+
npm run deploy # wrangler deploy
93+
npm run cf-typegen # regenerate Worker type definitions
94+
```
95+
96+
> The checked-in `test/index.spec.ts` is still the `create-cloudflare` "Hello World!" template and does not cover the real routes; it needs to be rewritten.
97+
98+
## How it fits the app
99+
100+
The Android *Transfer config* flow and the web Config Generator both encrypt the config locally, `PUT` it here, and surface the returned key (often as a QR code). The receiving device redeems the key once via `GET`, decrypts locally, and applies the config. The relay only ever holds opaque ciphertext for at most an hour, and only until it is read once.

0 commit comments

Comments
 (0)