Encrypted file transport for data you already keep backed up.
Local index. Verifiable restores. Explicit provider risk.
π Quick Start Β β’Β FAQ Β β’Β Why TAS? Β β’Β Features Β β’Β Security Β β’Β CLI Docs Β β’Β Docker / CI Β β’Β Changelog
TAS 3.0 β One
npm install, onetas init, thentas push yourfile.pdf. TAS encrypts content locally and sends round-trip-safe chunks through the Bot API. It is experimental transport, not a durable backup service: Telegram can limit, remove, or terminate access. Keep an independent backup.
TAS is a local-first CLI for moving encrypted file blobs through your own Telegram bots. Its SQLite index lives on your machine; content is encrypted before upload; completed mutations publish an encrypted recovery manifest you can use to rebuild the index.
Operating boundary. TAS is not unlimited storage, a backup guarantee, or a Telegram-supported cloud drive. Telegram provides no TAS quota, retention SLA, recovery service, or account guarantee. Its Bot Developer Terms also restrict external apps that diverge into cloud-storage use cases. Use TAS only for data that already has an independent backup.
TAS compresses, encrypts (AES-256-GCM), chunks, and uploads files to private bot chats. Your password stays local. Content, filenames, original sizes, and the recovery manifest are encrypted or omitted from Telegram-visible chunk metadata. Telegram still sees bot/chat identity, timing, chunk count, and encrypted sizes.
Your Machine Telegram Cloud
βββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
β β β β
β tas push secret.tar.gz ββββ gzip βββ β π Encrypted Blob #1 β
β tas mount ~/cloud ββββ AES-256 ββββ π Encrypted Blob #2 β
β tas sync start ββββ chunk βββ β π Encrypted Blob #3 β
β β β (Private Bot Chat) β
β tas pull secret.tar.gz ββββ decrypt ββββ β
β (SHA-256 verified) ββββ decomp ββββ β Stream on demand β
β β β β
βββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
SQLite Index Remote Bot Messages
Three commands to try it. Keep another copy of every file.
# 1. Install globally
npm install -g @nightowne/tas-cli
# 2. Connect your Telegram bot (guided wizard β takes ~60 seconds)
tas init
# 3. Start using it
tas push secret.pdf # Encrypt + compress + upload
tas pull secret.pdf # Download + decrypt + verify
tas list # See everything you've storedNeed a Telegram bot? Open Telegram β search
@BotFatherβ/newbotβ copy the token. That's it.
TAS is for people who want a small, inspectable command-line transport rather than another account, dashboard, or opaque sync daemon. It makes the important state visible and keeps the recovery path in your hands.
- A local source of truth. The index is SQLite, paths are exact, and nested directories behave consistently in sync and FUSE.
- A recovery story. TAS writes an authenticated, encrypted remote manifest after completed changes;
tas index rebuildcan restore the file-to-message map when the local index is gone. - A clean automation surface. Push, pull, search, tags, sync, and JSON output work from a shell, cron job, or CI runner.
- Deliberate multi-bot routing. Each chunk records its owner, so a pool is inspectable and reversible instead of a hidden round-robin trick.
- No false promise. The product is precise about the provider boundary: Telegram is not your storage vendor, and multi-bot mode is not a way around its rules.
Use Telegram storage exactly like a USB drive β drag and drop, open in any app.
tas mount ~/cloud # Mount your Telegram storage as ~/cloud
ls ~/cloud # Browse your encrypted files normally
cp report.pdf ~/cloud/ # Drop files in β auto-encrypted and uploaded
tas unmount ~/cloud # Clean unmount when doneNative FUSE is verified, not assumed: Linux needs
fuse/libfuse-dev. macOS uses current macFUSE and rebuilds the optional native addon against the installed system library on first install. Install Xcode Command Line Tools before TAS, then runtas doctor; mount is available only when its real mount β readdir β unmount smoke test passes.
Desktop file managers work with the normal private mount. Samba and other services run under a different local identity, so shared access must be enabled explicitly:
# Add this exact line to /etc/fuse.conf first: user_allow_other
tas mount /mnt/tg-drive --allow-other--allow-other exposes the mount to other local users subject to Unix permissions. Do not enable it on an untrusted multi-user host.
Register a local folder and TAS watches it. Any new or changed file is automatically encrypted and uploaded.
tas sync add ~/Documents # Register ~/Documents for auto-sync
tas sync start # Start the watcher (runs in background)
tas sync pull # Pull all synced files back down
tas sync status # See what's queued / synced / pendingTAS can distribute new chunks deterministically across multiple bots and records the owning bot on every chunk. Disabled bots remain configured for reads; a bot cannot be removed while indexed chunks or the recovery manifest depend on it.
tas bot add --name archive-2 # Interactive token/chat setup + risk acknowledgement
tas bot list # IDs, state, chat, and dependent chunk counts
tas bot disable archive-2 # Stop new writes; old chunks remain readable
tas bot enable archive-2
tas bot remove archive-2 # Refuses unless no data depends on itUse at your own risk. Multiple bots do not guarantee more quota, durability, ban avoidance, or Terms compliance. Do not use this feature to evade Telegram limits. All bots remain under Telegram's control, so this is distributionβnot redundancy.
Every completed storage mutation publishes a gzip-compressed, AES-256-GCM-authenticated manifest containing the file/chunk mapping and tags. Ephemeral share tokens are deliberately excluded. The latest pointer is stored in config.json.
tas index backup # Publish a fresh encrypted recovery point
tas index rebuild # Authenticate and rebuild index.dbKeep config.json and your password separately: recovery cannot discover the latest manifest if both the database and config are lost.
Generate time-limited, download-limited share links. Recipients get a clean dark-themed download page. Your password is never shared β files are decrypted on-the-fly by the local server.
tas share create report.pdf --expire 24h --max-downloads 5
# β http://localhost:3000/d/a1b2c3d4e5f6...
tas share create backup.tar.gz --expire 1h --max-downloads 1 # Burn-after-read
tas share list # See active links with expiry info
tas share revoke a1b2c3d4 # Revoke instantly, anytimetas tag add report.pdf work Q4 finance
tas tag add keys.env secrets production
tas search "report" # Search by filename pattern
tas search -t work # All files tagged "work"
tas search -t secrets # Quickly find your credentialstas doctor
# β Node.js 20.11.0
# β Config v3 (encrypted multi-bot token set)
# β Database: 42 files, 1.3 GB total across 28 chunks
# β Disk space: 50 GB free (32% used)
# β Encryption: AES-256-GCM Β· PBKDF2-SHA512 Β· 600,000 iterations (OWASP 2025)
# β FUSE runtime: mount β readdir β unmount passed
# β Telegram connectivity: 2/2 bots OK
# β¨ All systems go!TAS is fully scriptable. No interactive prompts needed when TAS_PASSWORD is set.
# Environment-based automation
export TAS_PASSWORD="your-password"
export TAS_DATA_DIR="/custom/path"
# Pipe to jq
tas list --json | jq '.[].filename'
tas list --json | jq '.[] | select(.size > 1000000)' # Files > 1MB
# GitHub Actions backup step
tas push db-backup-$(date +%Y%m%d).sql.gz
# cron: nightly backup at 2am
0 2 * * * TAS_PASSWORD=$SECRET tas push /var/backups/db.tar.gz
# JSON machine output everywhere
tas status --json
tas list --jsonFROM node:20-alpine
RUN npm install -g @nightowne/tas-cli
ENV TAS_PASSWORD=""
ENV TAS_DATA_DIR="/data"
VOLUME ["/data"]
CMD ["tas", "status"]# .github/workflows/backup.yml
name: Nightly Backup
on:
schedule:
- cron: '0 2 * * *'
jobs:
backup:
runs-on: ubuntu-latest
steps:
- name: Install TAS
run: npm install -g @nightowne/tas-cli
- name: Push backup
env:
TAS_PASSWORD: ${{ secrets.TAS_PASSWORD }}
TAS_DATA_DIR: ${{ runner.temp }}/tas-data
run: |
tas init --token ${{ secrets.TELEGRAM_BOT_TOKEN }} --chat ${{ secrets.TELEGRAM_CHAT_ID }}
tar czf backup-$(date +%Y%m%d).tar.gz ./important-data/
tas push backup-$(date +%Y%m%d).tar.gzTAS applies client-side authenticated encryption. TAS has no hosted service that receives your password, but this is not a formal zero-knowledge protocol and it does not hide all traffic metadata from Telegram.
| Layer | Implementation | Standard |
|---|---|---|
| Cipher | AES-256-GCM (authenticated encryption) | NIST FIPS 197 |
| Key Derivation | PBKDF2-SHA512, 600,000 iterations | OWASP 2025 |
| Salt | 32 bytes, crypto.randomBytes() β unique per file |
No rainbow tables |
| IV/Nonce | 12 bytes, crypto.randomBytes() β unique per file |
No nonce reuse |
| Auth Tag | 16 bytes GCM tag β any tampered bit = instant rejection | Tamper detection |
| Bot Tokens | Encrypted independently at rest in config.json |
Config v3 |
| Password Verification | crypto.timingSafeEqual() on both PBKDF2 and legacy paths |
Timing-safe |
| Config Permissions | chmod 600 config.json on creation |
No world-readable secrets |
| Recovery Manifest | gzip + AES-256-GCM; authenticated before SQLite import | Remote index recovery |
| Integrity | SHA-256 hash verified on completed downloads | Detects mismatch/corruption |
| Share Server | Binds 127.0.0.1 by default, XSS-escaped, RFC 6266 filenames |
LAN-safe |
chunk-000000.tas β 12.4 MB β caption: tas:c1:42:1/2
New uploads expose no user filename or original size in the document name, caption, or public WAS1 routing header. Telegram can still observe encrypted size, chunk count, timing, bot/chat identity, IP/network data, and message identifiers. Files uploaded by TAS 2.5 and older may still expose filename/size metadata until re-uploaded.
| Threat | Mitigated? | How |
|---|---|---|
| Telegram reads plaintext content | Mitigated | AES-256-GCM, assuming a strong password and uncompromised client |
| Telegram observes traffic metadata | Not mitigated | Bot/chat, timing, encrypted sizes, and chunk counts remain visible |
Someone steals config.json |
Partly mitigated | Tokens are encrypted; an offline password attack is still possible |
| Tampered download | Mitigated | GCM authentication plus final SHA-256 verification |
| Local machine compromise | Not mitigated | A process with password/filesystem access can read plaintext and tokens |
| Share link exposure | Limited | Localhost default, expiry, and download limits; the local server decrypts content |
Reliability mechanisms implemented by TAS (not an SLA):
| Feature | Implementation |
|---|---|
| Exponential Backoff | Auto-retry with jitter on Telegram 429 errors and network timeouts |
| Rate Limiting | One serialized send queue per configured bot within a TAS process; parallel TAS processes and Telegram's dynamic limits still apply |
| Integrity Verification | SHA-256 hash verified after every single download |
| Resume Uploads | Network-stage chunks are staged on disk and persisted in pending_uploads; tas resume continues them |
| Index Recovery | Authenticated encrypted remote manifest; tas index rebuild restores file/chunk ownership |
| Graceful Shutdown | SIGINT/SIGTERM handled; staged chunks and SQLite WAL reduce partial-state risk |
| Self-Diagnostics | Checks config/database/chunk limits, all bots, and a real native FUSE smoke mount |
Core Commands
tas init [--token T --chat ID --password PW] # π Wizard, or fully non-interactive for CI/Docker
tas push <files...> # β¬οΈ Encrypt + compress + upload (batch supported)
tas pull <file|hash> # β¬οΈ Download + decrypt + verify
tas list [-l] [--json] # π List all stored files
tas delete <file|hash> # ποΈ Remove from index (--hard removes from Telegram)
tas status [--json] # π Storage stats & database health
tas search <query> [-t tag] # π Find by filename or tag
tas resume # π Resume interrupted uploads
tas verify # β
Check every Telegram file reference
tas verify --deep # β
Download/decrypt/hash every file (slow and bandwidth-heavy)
tas doctor # π©Ί Full system health check
tas index backup # π§― Publish encrypted recovery manifest
tas index rebuild # π§― Restore index.db from that manifest
tas bot add|list|enable|disable|remove # π€ Manage experimental bot poolMount & Sync
# FUSE Mount (Linux/libfuse or macOS/current macFUSE)
tas mount <path> # Mount Telegram storage as a local folder
tas mount <path> --allow-other # Opt in to Samba/service access
tas unmount <path> # Clean unmount
# Dropbox-style Folder Sync
tas sync add <folder> # Register folder for auto-sync
tas sync start # Start watching for changes
tas sync pull # Download all synced files locally
tas sync status # Show sync queue and statusShare & Tags
# Expiring Share Links
tas share create <file> [--expire 1h|24h|7d] [--max-downloads N] [--host 0.0.0.0] [--port 3000]
tas share list # Active links with expiry countdown
tas share revoke <token> # Instantly revoke a share
# File Tagging
tas tag add <file> <tag> [tag2...]
tas tag remove <file> <tag>
tas tag list [tag] # List all tags, or files with a specific tagEnvironment Variables
TAS_PASSWORD="..." # Skip password prompts (CI/CD, cron, Docker)
TAS_DATA_DIR="/custom/path" # Override default ~/.tas data directorysrc/
βββ cli.js # Commander-based CLI β all commands defined here
βββ index.js # Core streaming upload/download pipeline
βββ manifest.js # Encrypted remote index backup/rebuild
βββ crypto/
β βββ encryption.js # AES-256-GCM + PBKDF2-SHA512 (600k iterations)
βββ db/
β βββ index.js # SQLite index: files, chunks, tags, shares, sync
βββ telegram/
β βββ client.js # Bot API wrapper β retry + serialized send queue
β βββ pool.js # Stable per-chunk multi-bot routing
βββ fuse/
β βββ mount.js # FUSE filesystem β mount Telegram as a local folder
βββ share/
β βββ server.js # HTTP server β expiring encrypted share links
βββ sync/
β βββ sync.js # fs.watch folder watcher β Dropbox-style auto-sync
βββ utils/
βββ download-stream.js # Shared TelegramβDecryptβDecompress pipeline
βββ compression.js # Smart gzip (skips already-compressed formats)
βββ chunker.js # 19 MiB payloads + metadata-free public WAS1 headers
βββ logical-path.js # Portable exact paths + virtual directory tree
βββ progress.js # Terminal progress bars with MB/s + ETA
βββ throttle.js # Bandwidth limiter (stream transform)
βββ branding.js # ASCII art + version display
βββ cli-helpers.js # Password management + config resolution
Tech stack: Node.js 18+ Β· better-sqlite3 Β· node-telegram-bot-api Β· fuse-native Β· Commander Β· Chalk Β· Ora Β· Inquirer
| Use Case | Example |
|---|---|
| π Personal document vault | Taxes, contracts, scans, receipts β encrypted |
| π Secrets & credentials | .env files, SSH private keys, API tokens |
| ποΈ Password manager sync | KeePass .kdbx, 1Password vaults, Bitwarden exports |
| π¦ Code project backups | Git bundles, build artifacts, config files |
| π¬ Private media archive | Photos, videos, music β encrypted & searchable |
| π Ephemeral file sharing | Burn-after-read links with download limits |
| πΎ Offsite backup | Nightly database dumps, system configs via cron |
| π€ CI/CD artifacts | Store build outputs, test reports, deployment keys |
Not appropriate for: the only copy of any data, mission-critical/business backups, regulated retention, team storage, or workloads that require an SLA. Telegram can remove messages or terminate access without giving TAS a recovery channel.
Yes. The Bot API supports sending documents, but that technical capability is not permission or a storage guarantee. Telegram's current Bot Developer Terms explicitly restrict external applications that diverge into cloud-storage use cases, prohibit circumventing rate limits, and allow bot/account termination. TAS cannot promise that one botβor a multi-bot poolβwill remain available.
Use TAS only at your own risk, do not use multiple bots to evade limits, follow all applicable laws and Telegram terms, and keep a tested independent backup. Encryption protects content confidentiality; it does not make the usage invisible or policy-compliant.
| π Not a replacement for backups | Telegram can purge old messages. Use TAS alongside, not instead of, real backup solutions. |
| π 19 MiB payload chunks | Hosted Bot API uploads permit more, but getFile documents only 20 MB downloads. TAS stays below the read limit. |
| π Multi-bot is experimental | It distributes chunks and preserves ownership mapping; it is not redundancy or ban protection. |
| π macOS needs a native build | Install current macFUSE and Xcode Command Line Tools before installing TAS. tas doctor must pass its real FUSE smoke test before you mount data. |
| π Recovery needs config | index.db can be rebuilt from the encrypted manifest only if config.json, password, manifest message, and owning bot survive. |
| π No versioning (yet) | Overwriting a file replaces the previous version. |
| π Internet required | Telegram-backed β offline access requires files pulled locally first. |
git clone https://github.com/ixchio/tas
cd tas && npm install
npm test # Run all 97 tests (crypto, paths, migrations, multi-bot, resume, manifest, sync, shares)
npm test -- --watch # Watch mode for active developmentTest coverage: streaming encrypt/decrypt roundtrips Β· cross-API compat (bufferβstream) Β· small-chunk stress testing Β· truncation/corruption error paths Β· Unicode filename handling Β· WAS1 binary header parsing Β· timing-safe comparison paths
PRs welcome! See CONTRIBUTING.md for guidelines.
TAS is open source and contributions are genuinely appreciated:
- π Found a bug? Open an issue β include
tas doctoroutput - π‘ Have a feature idea? Start a discussion
- π§ Want to contribute code? Fork β branch β PR β π
- β Just want to help? A GitHub star dramatically increases discoverability
MIT β use it, fork it, ship it, sell it. Do whatever you want with it.
If TAS fits your workflow, you might also find these useful:
- rclone β rsync for cloud storage (dozens of backends)
- restic β encrypted, deduplicated backup program
- age β simple, modern file encryption tool
- magic-wormhole β encrypted file transfer between machines
Built with β and stubbornness by @ixchio
If TAS saved you money, a β on GitHub is the best way to say thanks β it helps others find the project.
