A Node.js tool that uses Telegram private channels as encrypted file storage, with Telegram bots acting as parallel workers.
Files are split into chunks, each chunk is encrypted with AES-256-GCM before upload. Chunk metadata is stored in a local SQLite database.
- Chunked upload/download for files larger than Telegram's 20 MB bot download limit
- AES-256-GCM encryption with PBKDF2 key derivation
- SHA-256 integrity checks for chunks and whole files
- Multiple bots and channels for parallel throughput
- Rate limiting to avoid Telegram flood errors
- Resume-friendly chunk uploading
- Download verification without writing final output
- Database backup and encrypted metadata export/import
doctorcommand for config validation
You can use this project in three ways. Pick the one that fits your workflow.
Use this if you want the included command-line tool and source access.
git clone https://github.com/shadowlite07/encrypted-telegram-drive.git
cd encrypted-telegram-drive
npm install
node src/cli.js doctor
node src/cli.js statusUse this if you want the telegram-drive command without manually keeping the source folder.
npm install -g github:shadowlite07/encrypted-telegram-drive
telegram-drive doctor
telegram-drive statusUse this if you want to call the API from your own JavaScript or Node.js project.
npm install github:shadowlite07/encrypted-telegram-driveconst { createDrive, loadConfig } = require('encrypted-telegram-drive');
const config = loadConfig('./config.json');
const drive = createDrive(config, process.env.DRIVE_KEY, { quiet: true });
const folder = await drive.folders.create('Work');
const file = await drive.files.upload('./file.bin', { folderId: folder.id });
await drive.files.verify(file.fileId);
await drive.files.download(file.fileId);
await drive.close();If you are developing locally in this repository, run npm link first, or require:
const { createDrive } = require('./src/index.js');Drive setup:
Create the config first (this also loads .env automatically):
const { createDrive, loadConfig } = require('encrypted-telegram-drive');
const config = loadConfig('./config.json');
const drive = createDrive(config, process.env.DRIVE_KEY, { quiet: true });You can also pass a config object directly:
const { createDrive } = require('encrypted-telegram-drive');
const config = {
bots: [
{ id: 'b1', alias: 'bot1', display: 'Vault Bot 1', token: process.env.BOT1_TOKEN }
],
channels: [
{ id: process.env.DRIVE_CHANNEL_1_ID, alias: 'drive1', display: 'Vault Channel 1' }
],
chunkSizeBytes: 16 * 1024 * 1024,
concurrency: 4,
rate: { perBotChatPerMinute: 18, globalPerSecond: 25 },
dbPath: './data/drive.db',
workDir: './.chunks'
};
const drive = createDrive(config, process.env.DRIVE_KEY, { quiet: true });Folders:
await drive.folders.create('Work');
await drive.folders.create('Invoices', { parent: folderId });
await drive.folders.list();
await drive.folders.list(parentId);
await drive.folders.get(folderId);
await drive.folders.rename(folderId, 'New Name');
await drive.folders.delete(folderId);Files:
await drive.files.upload('./file.bin', {
folderId,
fresh: false,
cleanupOnFail: false,
onProgress: (progress) => console.log(progress)
});
await drive.files.list({ folderId, all: false, recursive: false });
await drive.files.get(fileId);
await drive.files.move(fileId, folderId);
await drive.files.move(fileId, 'root');
await drive.files.verify(fileId);
await drive.files.download(fileId);
await drive.files.download(fileId, './restored.bin');
await drive.files.delete(fileId);Maintenance and portability:
await drive.cleanupFailed();
await drive.backup('./backups/drive.db.copy');
await drive.exportBundle('./drive-bundle.enc');
await drive.importBundle('./drive-bundle.enc');
await drive.portable('./telestore-portable');
const health = await drive.doctor();
drive.close();config.jsonuses${ENV_VAR}placeholders and is safe to commit..envis private and must not be committed..env.exampledocuments the required variables.- Do not put real bot tokens or channel IDs directly in
config.jsonif you plan to push it.
- Node.js 24 or newer
- Telegram bot(s) created via
@BotFather - Private Telegram channel(s) where every bot is an admin
- A strong
DRIVE_KEY
npm installnode src/cli.js initThis creates:
config.jsonwith${ENV_VAR}placeholders.envfrom.env.example
Edit .env:
BOT1_TOKEN=123456:ABC-REPLACE-1
BOT2_TOKEN=123456:ABC-REPLACE-2
BOT3_TOKEN=123456:ABC-REPLACE-3
DRIVE_CHANNEL_1_ID=-100REPLACE-1
DRIVE_CHANNEL_2_ID=-100REPLACE-2
DRIVE_CHANNEL_3_ID=-100REPLACE-3
DRIVE_KEY=replace-with-a-long-base64-keyDo not put real secrets in config.json. It is tracked by Git and already references these environment variables:
{
"bots": [
{ "id": "b1", "alias": "bot1", "display": "Vault Bot 1", "token": "${BOT1_TOKEN}" }
],
"channels": [
{ "id": "${DRIVE_CHANNEL_1_ID}", "alias": "drive1", "display": "Vault Channel 1" }
]
}You only need to edit .env. Changing config.json is only needed if you want different folder settings, chunk size, concurrency, or database paths.
$env:DRIVE_KEY = "your-strong-passphrase"Generate one:
$bytes = New-Object byte[] 64
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rng.GetBytes($bytes)
$env:DRIVE_KEY = [Convert]::ToBase64String($bytes)
$env:DRIVE_KEYValidate DRIVE_KEY, config, bot tokens, channel access, admin status, and the local database.
node src/cli.js doctorSplit the file into encrypted chunks and upload them to Telegram.
node src/cli.js upload .\file.bin
node src/cli.js upload .\file.bin --fresh
node src/cli.js upload .\file.bin --cleanup-on-fail
node src/cli.js upload .\file.bin --folder <folderId>Create an empty folder for organizing files.
node src/cli.js folder create Work
node src/cli.js folder create Invoices --parent <folderId>Rename an existing folder.
node src/cli.js folder rename <folderId> "New Name"Delete an empty folder.
node src/cli.js folder delete <folderId>List folders in the root or under another folder.
node src/cli.js folders
node src/cli.js folders <folderId>Move a stored file into a folder, back to root, or remove its folder assignment.
node src/cli.js move <fileId> <folderId>
node src/cli.js move <fileId> rootList folders and files. Defaults to the root folder.
node src/cli.js list
node src/cli.js list --folder <folderId>
node src/cli.js list --allDownload and reassemble a stored file. If outPath is omitted, saves to your Downloads folder.
node src/cli.js download <fileId>
node src/cli.js download <fileId> .\restored.binDownload chunks to a temporary file, verify all hashes, then delete the temporary file.
node src/cli.js verify <fileId>Delete a stored file and its Telegram chunks.
node src/cli.js delete <fileId>Delete all failed or incomplete uploads and their chunks.
node src/cli.js cleanupCreate a safe SQLite backup of the metadata database.
node src/cli.js backup
node src/cli.js backup .\backups\drive.db.copyCreate an encrypted metadata bundle for transfer or recovery.
node src/cli.js export --out .\drive-bundle.encRestore metadata from an encrypted bundle.
node src/cli.js import --in .\drive-bundle.encCreate a transferable folder containing config, database, bundle, and restore notes.
node src/cli.js portable .\telestore-portablePoll Telegram updates to help find channel IDs.
node src/cli.js discover --bot b1 --seconds 20- Bot API file downloads are limited to about 20 MB.
- The default chunk size is 16 MB.
- Group/channel rate limits are approximately 20 messages per minute per bot per chat.
- The app defaults are conservative: 18/min per bot+channel and 25 global requests/sec.
DRIVE_KEYis never uploaded and is required for decryption.- Losing
DRIVE_KEYmeans the encrypted chunks cannot be recovered. - The local SQLite database is the source of truth for chunk metadata.
- Back up both
config.jsonanddata/drive.db. - Do not commit secrets or the database.
- Bot token is invalid or still the placeholder value.
- Recreate or copy the token from
@BotFather.
- Channel ID is wrong.
- The bot is not an admin in that channel.
- Lower
concurrency,globalPerSecond, andperBotChatPerMinute.
- Keep
chunkSizeBytesat 16 MB or lower for standard Bot API limits.
$token = "YOUR_REAL_TOKEN"
$response = Invoke-RestMethod -Uri "https://api.telegram.org/bot$token/getUpdates?timeout=10&allowed_updates=%5B%22channel_post%22,%22message%22,%22my_chat_member%22%5D"
$response | ConvertTo-Json -Depth 8Post a test message in the channel during the request, or forward a channel message to @userinfobot.
config.json
data/
.chunks/
.env