telstore 0.1.5 → 0.1.6
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/README.md +74 -175
- package/package.json +1 -1
- package/src/caption.js +2 -2
package/README.md
CHANGED
|
@@ -1,78 +1,68 @@
|
|
|
1
1
|
# telstore
|
|
2
2
|
|
|
3
|
-
Split large files into
|
|
3
|
+
Split large files into chunks, store them on Telegram, and restore them byte-for-byte.
|
|
4
|
+
|
|
5
|
+
telstore signs in with your own Telegram account over MTProto, not a bot: the Bot API caps
|
|
6
|
+
uploads at 50MB per file, a user account gets 2GB.
|
|
7
|
+
|
|
8
|
+
Needs Node.js 18+ and an `api_id`/`api_hash` from <https://my.telegram.org> → API
|
|
9
|
+
development tools; `login` asks for those, your phone number and the verification code.
|
|
4
10
|
|
|
5
11
|
## Quick start
|
|
6
12
|
|
|
7
13
|
```bash
|
|
8
|
-
npx telstore login
|
|
9
|
-
npx telstore config chat @my_backups
|
|
10
|
-
npx telstore data.tar
|
|
11
|
-
npx telstore
|
|
12
|
-
npx telstore config # every setting and where its value comes from
|
|
13
|
-
npx telstore status # account, destination, unfinished backups
|
|
14
|
-
npx telstore list # what is already stored in the destination
|
|
14
|
+
npx telstore login # once only
|
|
15
|
+
npx telstore config chat @my_backups # where backups go, from now on
|
|
16
|
+
npx telstore data.tar # split it and send it there
|
|
17
|
+
npx telstore list # what is already in the destination
|
|
15
18
|
npx telstore restore telstore-20260905-7f3a91
|
|
16
|
-
npx telstore delete telstore-20260905-7f3a91 # take it back out of the chat, for good
|
|
17
|
-
npx telstore token # a session token for a machine you do not trust
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
- Node.js 18 or newer.
|
|
23
|
-
- An `api_id` and `api_hash` from <https://my.telegram.org> → API development tools. The `login` command asks for both, plus your phone number and the verification code.
|
|
21
|
+
## Commands
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
| Command | What it does |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `telstore login` | Log in to Telegram. Add `--token` to log in with a session token instead. |
|
|
26
|
+
| `telstore <file>` | Split the file and upload it. Prints the `backupId` you restore with. |
|
|
27
|
+
| `telstore list` | The backups stored in the destination, newest first. |
|
|
28
|
+
| `telstore restore <backup-id>` | Download every chunk and reassemble the file. |
|
|
29
|
+
| `telstore delete <backup-id>` | Remove a backup's chunks and manifest from the chat, for good. |
|
|
30
|
+
| `telstore status` | Account, destination, and unfinished backups. |
|
|
31
|
+
| `telstore config` | Show or change settings. |
|
|
32
|
+
| `telstore token` | Print a session token for a machine you do not trust. |
|
|
33
|
+
| `telstore logout` | Remove the locally stored session. |
|
|
26
34
|
|
|
27
|
-
## Settings
|
|
35
|
+
## Settings
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
`config` writes; flags do not. A flag applies to the run you typed it on and changes nothing
|
|
38
|
+
on disk, so `--to @elsewhere` sends one backup elsewhere without moving the destination for
|
|
39
|
+
the next one.
|
|
32
40
|
|
|
33
41
|
```bash
|
|
34
|
-
npx telstore config
|
|
35
|
-
npx telstore config
|
|
36
|
-
npx telstore config chunkSize
|
|
37
|
-
npx telstore config chunkSize --unset # back to the default
|
|
42
|
+
npx telstore config # every setting, and whether it is yours or a default
|
|
43
|
+
npx telstore config chunkSize 500MB # change it for good
|
|
44
|
+
npx telstore config chunkSize --unset # back to the default
|
|
38
45
|
```
|
|
39
46
|
|
|
40
47
|
| Setting | Flag | Default | Meaning |
|
|
41
48
|
|---|---|---|---|
|
|
42
|
-
| `chat` | `--to` | none | `@username`, `-100123…`, or `me
|
|
43
|
-
| `chunkSize` | `--chunk-size` | `1800MB` | e.g. `1.8GB`, `500MB`.
|
|
44
|
-
| `uploadConcurrency` | `--upload-concurrency` | `32` | 512KB parts
|
|
45
|
-
| `downloadConcurrency` | `--download-concurrency` | `8` | 8MB slices
|
|
46
|
-
| `limit` | `--limit` | `20` | How many backups `list` shows
|
|
47
|
-
| `verbose` | `--verbose` | off | Show the Telegram client's own connection logs
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
defaults to the basename in the manifest. Relative paths resolve against the current directory.
|
|
54
|
-
|
|
55
|
-
`config` reads and writes only settings — the `api_id`, `api_hash` and session that `login`
|
|
56
|
-
stores in the same file are not reachable from it. A value is checked before it is written,
|
|
57
|
-
so `config chunkSize 9GB` is refused there and then rather than at the start of a long upload.
|
|
49
|
+
| `chat` | `--to` | none | `@username`, `-100123…`, or `me` |
|
|
50
|
+
| `chunkSize` | `--chunk-size` | `1800MB` | e.g. `1.8GB`, `500MB`. Ceiling 1950MB. |
|
|
51
|
+
| `uploadConcurrency` | `--upload-concurrency` | `32` | 512KB parts in parallel while uploading, 1–64 |
|
|
52
|
+
| `downloadConcurrency` | `--download-concurrency` | `8` | 8MB slices in parallel while restoring, 1–64 |
|
|
53
|
+
| `limit` | `--limit` | `20` | How many backups `list` shows |
|
|
54
|
+
| `verbose` | `--verbose` | off | Show the Telegram client's own connection logs |
|
|
55
|
+
|
|
56
|
+
Three flags have no setting behind them: `--out <path>` names where one restore writes,
|
|
57
|
+
`--yes` skips the confirmation `delete` asks, and `--token` takes no value — a token written
|
|
58
|
+
on the command line would sit in `ps` and in that machine's shell history, so it is pasted at
|
|
59
|
+
a prompt that does not echo.
|
|
58
60
|
|
|
59
61
|
## What the chat looks like
|
|
60
62
|
|
|
61
|
-
Every chunk goes up as a document captioned `📦 <backupId> · 3/12`,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
🗄 data.tar
|
|
66
|
-
💾 21.4 GB · 12 chunks
|
|
67
|
-
🆔 telstore-20260905-7f3a91
|
|
68
|
-
📅 2026-09-05 16:40 UTC
|
|
69
|
-
|
|
70
|
-
↩ npx telstore restore telstore-20260905-7f3a91
|
|
71
|
-
#telstore
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
`npx telstore list` reads those cards straight out of the chat — one search, no downloads —
|
|
75
|
-
and lays them out as a table:
|
|
63
|
+
Every chunk goes up as a document captioned `📦 <backupId> · 3/12`, followed by a manifest
|
|
64
|
+
carrying a summary card — file name, size, id, date and the restore command. `list` reads
|
|
65
|
+
those cards straight out of the chat, one search and no downloads:
|
|
76
66
|
|
|
77
67
|
```
|
|
78
68
|
Destination https://web.telegram.org/k/#@my_backups
|
|
@@ -84,141 +74,50 @@ telstore-20260901-9de447 photos.zip 940.3 MB 1 2026-09-01
|
|
|
84
74
|
2 backups. Restore with: npx telstore restore <backup-id>
|
|
85
75
|
```
|
|
86
76
|
|
|
87
|
-
|
|
88
|
-
says nothing — `list` reports what the chat holds and never fills gaps with guesses.
|
|
89
|
-
|
|
90
|
-
## How it works
|
|
91
|
-
|
|
92
|
-
Every run mints a `backupId`. The file is read directly by offset — no temporary copies — and uploaded as documents named `<backupId>.partNNNN`. Once every chunk is up, telstore sends a JSON manifest listing the message id and sha256 of each one. Restore needs only the `backupId`: it finds the manifest in the chat, downloads each chunk to its exact position in a `.partial` file, checks every chunk's sha256 and size, and renames it to the real file only after *all* chunks match.
|
|
93
|
-
|
|
94
|
-
If the connection drops during an **upload**, just run the same command again — progress lives in `~/.telstore/state/` and finished chunks are skipped, keeping the same `backupId`. Two things to know about rerunning:
|
|
95
|
-
|
|
96
|
-
- Running again against a destination that differs from the one in the unfinished progress makes telstore **refuse to run** rather than silently redirect — one backup cannot be split across two destinations. The error names the chat to pass as `--to` to carry on, or the state file to delete to start a new backup. It reads the same whether the mismatch came from a flag or from your configured `chat`.
|
|
97
|
-
- Running again **without** `--chunk-size` resumes at the size the backup started with, whatever your configured `chunkSize` says today. A setting is what to use when nobody asks for anything; it is not somebody asking.
|
|
98
|
-
- Running again **with** a `--chunk-size` that differs from that size makes telstore **refuse to run**: the chunks already in the chat were cut that way and cannot be re-cut. Drop the flag to carry on, or delete the state file to start a new backup — which leaves the chunks already sent in the chat with nothing pointing at them.
|
|
99
|
-
|
|
100
|
-
`Ctrl-C` during an upload names the backup it was working on, so `telstore status` and a later `restore` have something to go on. telstore keeps the **20 most recent** unfinished backups in `~/.telstore/state/`; starting a new one past that drops the oldest record and says which id it dropped. Only the local record goes — the chunks that backup sent stay in the chat, searchable by that id, but it can no longer be resumed.
|
|
101
|
-
|
|
102
|
-
**Restore keeps no state to resume from.** Pressing `Ctrl-C` mid-restore saves nothing — running again starts over.
|
|
103
|
-
|
|
104
|
-
## Deleting a backup
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
npx telstore delete telstore-20260905-7f3a91
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
It prints what it is about to destroy, asks once, and then removes every chunk message and
|
|
111
|
-
the manifest from the chat and drops the local record if there is one. `--yes` skips the
|
|
112
|
-
question. **There is no undo** — Telegram is the only copy.
|
|
113
|
-
|
|
114
|
-
It also works on a backup that never finished: those have chunks in the chat but no manifest,
|
|
115
|
-
so `list` cannot see them and only `status` knows they exist. `delete` reads the local record
|
|
116
|
-
instead and clears both.
|
|
77
|
+
## Resuming an upload
|
|
117
78
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
runs the backup still shows up in `list`, and a `restore` of it fails loudly rather than
|
|
123
|
-
handing over a partial file.
|
|
79
|
+
Progress lives in `~/.telstore/state/` (the 20 most recent), so running the same command
|
|
80
|
+
again skips the finished chunks and keeps the same `backupId`. A resumed backup keeps the
|
|
81
|
+
chunk size it started with; passing a `--chunk-size` or a destination that differs from its
|
|
82
|
+
own makes telstore refuse to run rather than re-cut or redirect it silently.
|
|
124
83
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
manifest or local record giving a message id that is not a whole positive number. Neither
|
|
128
|
-
deletes anything at all. A manifest too damaged for `restore` to use *can* still be deleted —
|
|
129
|
-
that is usually the one you want gone.
|
|
84
|
+
**Restore keeps no state** — `Ctrl-C` mid-restore saves nothing, running again starts over.
|
|
85
|
+
And `delete` has **no undo**: Telegram is the only copy.
|
|
130
86
|
|
|
131
87
|
## Running on a machine you do not trust
|
|
132
88
|
|
|
133
|
-
`login` leaves your
|
|
134
|
-
|
|
89
|
+
`login` leaves your session on disk in plain text. When the machine is not yours, print a
|
|
90
|
+
**session token** on one that is and log in with that instead:
|
|
135
91
|
|
|
136
92
|
```bash
|
|
137
|
-
# on
|
|
138
|
-
npx telstore token
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# on the other machine
|
|
142
|
-
npx telstore login --token # paste the token, then type the passphrase
|
|
143
|
-
npx telstore restore telstore-20260905-7f3a91
|
|
144
|
-
npx telstore logout # when you are done
|
|
93
|
+
npx telstore token # on your own machine: asks for a passphrase, prints one line
|
|
94
|
+
npx telstore login --token # on the other one: paste the token, then the passphrase
|
|
95
|
+
npx telstore logout # when you are done
|
|
145
96
|
```
|
|
146
97
|
|
|
147
98
|
The token holds your `api_id`, `api_hash`, session and settings, encrypted with the
|
|
148
|
-
passphrase (AES-256-GCM,
|
|
149
|
-
|
|
150
|
-
passphrase
|
|
151
|
-
|
|
152
|
-
`login --token` stores the token exactly as it arrived, so **the session never exists on that
|
|
153
|
-
machine in a form anyone can read**. Every command that talks to Telegram asks for the
|
|
154
|
-
passphrase and opens it in memory:
|
|
155
|
-
|
|
156
|
-
```json
|
|
157
|
-
{ "sealed": "tls1.…", "settings": { "chat": "@my_backups" } }
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
`npx telstore status` shows which of the two you are running:
|
|
161
|
-
|
|
162
|
-
```
|
|
163
|
-
Session /home/you/.telstore/config.json (sealed — opened with a passphrase)
|
|
164
|
-
Account Sho (@shovity)
|
|
165
|
-
Destination https://web.telegram.org/k/#@my_backups
|
|
166
|
-
Unfinished none
|
|
167
|
-
```
|
|
99
|
+
passphrase (AES-256-GCM, scrypt), as one line of base64url that survives a chat message or a
|
|
100
|
+
QR code. `login --token` stores it exactly as it arrived and every command asks for the
|
|
101
|
+
passphrase, so the session never exists on that machine in readable form.
|
|
168
102
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
which lives inside it.
|
|
173
|
-
- **State files, yes.** An upload writes `~/.telstore/state/<key>.json` so it can be resumed
|
|
174
|
-
after an interruption. It holds the file path, the chunk sizes and the message ids — no
|
|
175
|
-
secret — and losing it would strand chunks in the chat with nothing able to name them.
|
|
176
|
-
|
|
177
|
-
### Things worth knowing before you use one
|
|
178
|
-
|
|
179
|
-
- **`--token` takes no value, on purpose.** A token written on the command line would sit in
|
|
180
|
-
`ps` for the whole life of the command and stay in that machine's shell history afterwards.
|
|
181
|
-
It is pasted at a prompt that does not echo it.
|
|
182
|
-
- **The passphrase may be left empty**, and then the token says so on its face: it starts
|
|
183
|
-
`tls0.` instead of `tls1.`, `token` warns, and `login` stores the session in plain text
|
|
184
|
-
exactly as an ordinary login would. telstore will not write a file that looks encrypted and
|
|
185
|
-
is not.
|
|
186
|
-
- **telstore cannot take a token back.** There is no expiry and no revocation. Like `logout`,
|
|
187
|
-
it says nothing to Telegram — to end the session for good, open Telegram → Settings →
|
|
188
|
-
Devices (Active sessions) and terminate it. Every copy of the token dies with it.
|
|
189
|
-
- **A wrong passphrase and a damaged token are the same event** to AES-GCM: one failed
|
|
190
|
-
authentication check. telstore names both possibilities rather than guessing which it was.
|
|
103
|
+
There is no expiry and no revocation: to end a session for good, terminate it under Telegram
|
|
104
|
+
→ Settings → Devices. An empty passphrase is allowed, and then the token says so on its face
|
|
105
|
+
— it starts `tls0.` and `login` stores the session in plain text.
|
|
191
106
|
|
|
192
107
|
## Limits worth knowing
|
|
193
108
|
|
|
194
|
-
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
`~/.telstore/config.json` (mode 600) holds `apiId`, `apiHash` and the session at the top level, with everything `config` manages under `settings`:
|
|
202
|
-
|
|
203
|
-
```json
|
|
204
|
-
{
|
|
205
|
-
"apiId": 123456,
|
|
206
|
-
"apiHash": "…",
|
|
207
|
-
"session": "…",
|
|
208
|
-
"settings": { "chat": "@my_backups", "chunkSize": 524288000 }
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
A machine logged in with `npx telstore login --token` holds `sealed` instead of those three
|
|
213
|
-
fields, and nothing else about the account:
|
|
214
|
-
|
|
215
|
-
```json
|
|
216
|
-
{ "sealed": "tls1.…", "settings": { "chat": "@my_backups" } }
|
|
217
|
-
```
|
|
109
|
+
- **Your data is not encrypted.** Don't upload anything you would mind sitting on someone
|
|
110
|
+
else's infrastructure — the one thing telstore encrypts is a session token, and that
|
|
111
|
+
protects your login rather than your files.
|
|
112
|
+
- A chunk cannot exceed 1950MB: Telegram accepts at most 4000 parts of 512KB per file.
|
|
113
|
+
- Deleting a chunk message in the Telegram app destroys the backup, and keeping the
|
|
114
|
+
`backupId` is what saves you hunting for its manifest in the chat by hand.
|
|
218
115
|
|
|
219
|
-
|
|
220
|
-
|
|
116
|
+
Settings and credentials live in `~/.telstore/config.json`, mode 600 — `apiId`, `apiHash` and
|
|
117
|
+
the session at the top level (or a single `sealed` blob after `login --token`), everything
|
|
118
|
+
`config` manages under `settings`. Editing it by hand is fine: a value that cannot be used is
|
|
119
|
+
named on the next run, with the file and the key.
|
|
221
120
|
|
|
222
|
-
|
|
121
|
+
## License
|
|
223
122
|
|
|
224
|
-
|
|
123
|
+
MIT
|
package/package.json
CHANGED
package/src/caption.js
CHANGED
|
@@ -24,7 +24,7 @@ export function chunkCaption({ id, number, total }) {
|
|
|
24
24
|
|
|
25
25
|
export function manifestCaption({ id, name, size, chunks, createdAt }) {
|
|
26
26
|
return [
|
|
27
|
-
|
|
27
|
+
`📄 ${oneLine(name)}`,
|
|
28
28
|
`💾 ${formatBytes(size)} · ${chunks} chunk${chunks === 1 ? '' : 's'}`,
|
|
29
29
|
`🆔 ${id}`,
|
|
30
30
|
`📅 ${utcMinutes(createdAt)}`,
|
|
@@ -45,7 +45,7 @@ function marker(lines, emoji) {
|
|
|
45
45
|
export function parseManifestCaption(text) {
|
|
46
46
|
const lines = String(text ?? '').split('\n')
|
|
47
47
|
|
|
48
|
-
const name = marker(lines, '
|
|
48
|
+
const name = marker(lines, '📄')
|
|
49
49
|
const totals = marker(lines, '💾')
|
|
50
50
|
const id = marker(lines, '🆔')
|
|
51
51
|
const createdAt = marker(lines, '📅')
|