Go + Wails desktop app I built as my first Go project. It uses a private Telegram channel like a “cloud drive” so you can upload, download, and organize files into folders (for learning/education only).
Go
97
310 commits
updated Oct 3, 2026
Your files, organized around your own Telegram account.
A desktop and mobile file manager with media streaming, encrypted photo and video backup, shared drives, and a scriptable CLI.
Download TDrive · Get started · Features · Photo backup · CLI and agents · Build
[!IMPORTANT] TDrive is an independent educational project and is not affiliated with Telegram. Use it responsibly and follow Telegram's terms. Do not treat Telegram or TDrive as the only copy of irreplaceable data.
Download packages from GitHub Releases, not from unofficial mirrors. Open the release's Assets list and choose the package for your device.
| Platform | App package | CLI | Distribution |
|---|---|---|---|
| macOS Apple silicon | *-macos-arm64.zip | *-darwin-arm64-cli.tar.gz | GitHub Releases |
| Windows amd64 | *-windows-amd64-setup.exe or portable .zip | *-windows-amd64-cli.zip | GitHub Releases |
| Linux amd64 | *-x86_64.AppImage | *-linux-amd64-cli.tar.gz | GitHub Releases |
| Android ARM64 | *-android-arm64.apk when published | Not available | GitHub only, not the Play Store |
| iOS | No public download yet | Not available | Source builds only |
The Windows installer runs per user without administrator rights. The portable zip contains the same application without the installer.
Linux AppImages require glibc 2.35 or newer (Ubuntu 22.04 or a compatible distribution). For the first upgrade from v2.0.0 or earlier, download TDrive-v2.0.1-x86_64.AppImage manually, make it executable, and launch it. Earlier versions' in-app updater expects the old filename; v2.0.1 recognizes the new name for subsequent updates. Your existing account and drive data are retained.
Updates are manual: download a newer APK from GitHub and install it over the existing release build. Official release updates use the same signing certificate.
TDrive is not notarized by Apple. If macOS cannot verify the developer, proceed only after checking that you downloaded the app from this repository's releases and trust it.
This adds an exception for the app; it does not require disabling Gatekeeper. See Apple's explanation of these warnings.
TDrive stores your API credentials locally; it does not ship a shared project-wide Telegram credential. Keep the API hash, login session, and vault password private.
Already use TDrive on another device? Sign into the same Telegram account and select the same personal-drive channel. If encryption is configured, unlock it with your existing vault password. A new device does not need a new encryption password for that drive.
![]() | ![]() | ![]() |
| Gallery | File viewer | Encrypted file unlock |
.zip, .tar, and .tar.gz archives, with optional extraction.Open Photo & video backup from the desktop account menu or the Android Account tab. Choose your folders and start backup. Files go into Photo backup / <device> / <source> inside My Drive, under your selected destination parent if configured.
| Device | Sources |
|---|---|
| Desktop | Selected local folders, including subfolders |
| Android | Permitted photos and videos in selected folders on device storage |
| iOS source build | Backup source selection is not available |
Backup is encrypted and personal-drive-only. It reuses your drive's existing vault: unlock it with the same password used on your other devices. If that drive has no vault yet, TDrive asks you to set one up.
Backup never deletes device originals. Removing a source does not delete files already uploaded to TDrive. Pause, queue state, and confirmed uploads are remembered across restarts.
Mobile permissions determine which media TDrive can see. Discovery runs while the app is active; an in-flight upload may continue with a native background allowance. Backup is not a continuously scheduled uploader and cannot continue after the app process is terminated. Keep the app open for the initial backup of a large library.
For the detailed recovery, staging, and platform limits, see the backup documentation.
TDX1|... metadata messages.[!WARNING]
TDX1|...messages are part of the drive history. Do not edit or delete them in the Telegram app; doing so can damage or desynchronize the projected filesystem.
Manual uploads on My Drive can use client-side XChaCha20-Poly1305 encryption. Photo and video backup always uses encryption. TDrive encrypts file contents before sending them to Telegram and decrypts them after the correct vault password is entered.
Important limits:
Shared drives support file and folder upload, download, rename, move, and delete. Members can see who uploaded a file and can organize the shared namespace.
Invite links should be treated like passwords. Use approval-required links when membership needs review, and revoke links that should no longer grant access.
Deleting a shared folder also deletes its contained files after confirmation. Shared drives mount read-only even though they remain writable through the app and CLI.
The CLI is available for macOS, Linux, and Windows amd64. Most commands automatically start a local daemon that keeps the Telegram connection, sync state, and unlocked vault key warm between commands.
The GUI and CLI daemon cannot use the same backend state simultaneously. Close the GUI before using CLI commands; the CLI reports this conflict instead of starting a second backend.
Download and extract the CLI archive for your platform. Open a terminal in the extracted TDrive-...-cli directory, then run:
./install-cli.sh
Reload the shell if the installer updated its configuration, then set up and log in:
tdrive version
tdrive setup --api-id YOUR_ID --api-hash-stdin
tdrive login +15551234567
tdrive whoami
tdrive ls
Extract the *-windows-amd64-cli.zip release asset and run tdrive.exe from PowerShell:
.\tdrive.exe setup --api-id YOUR_ID --api-hash-stdin
.\tdrive.exe login +15551234567
.\tdrive.exe whoami
tdrive drives
tdrive drive use <name|id>
tdrive ls -l
tdrive mkdir -p /Photos
tdrive put photo.jpg /Photos/
tdrive get /Photos/photo.jpg .
tdrive mkdir -p /Imports
tdrive put --extract archive.zip /Imports/
tdrive mv /old /new
tdrive rm -r /folder
tdrive sync
tdrive rebuild
tdrive unlock
tdrive mount
tdrive mount status
tdrive mount stop
Folder and archive imports require the destination folder to exist. Single-file uploads can create or rename the final file path.
Shared-drive commands include drive create, drive link, drive join, drive requests, drive approve, drive deny, and drive leave. Run tdrive help or tdrive <command> --help for the complete command reference.
Start with the offline version and command manifest. Neither needs a daemon or Telegram connection. tdrive commands --json is the authoritative list of JSON-supported commands, flags, and mutation behavior.
tdrive version --json
tdrive commands --json
After interactive setup and login, use JSON output and explicit drive IDs for scripts:
tdrive drives --json
tdrive ls /Photos --drive-id 123456789 --json --non-interactive
tdrive put photo.jpg /Photos/photo.jpg --drive-id 123456789 --json --non-interactive
Use the numeric drive ID returned by tdrive drives --json, replacing the example ID above. Drive-scoped JSON commands require --drive-id and canonical absolute remote paths. They do not change the daemon's shared active drive or working directory, so concurrent scripts can target different drives safely. Human-readable output remains the default; --json (or --output json) is opt-in and rejects unsupported commands.
Each successful JSON command writes one schema-versioned object to stdout (schema_version, ok, command, data), with no progress text. An error writes one object to stderr (schema_version, ok: false, error with code, message, retryable, and optional hint). Branch on error.code, not the message.
| Exit code | Meaning |
|---|---|
0 | Success |
1 | Other operation failure |
2 | Invalid or unsupported command |
3 | Required input or authentication |
4 | Not found |
5 | Conflict or required confirmation |
6 | Timeout or unavailability |
--non-interactive never prompts. Vault unlock requires --password-stdin in JSON or non-interactive mode.rm and rebuild require --yes. Non-interactive text mode also requires it for destructive drive actions and full logout. There is no dry-run option.put accepts one regular local file. JSON get requires an explicit local file path and refuses to overwrite it unless --yes is given. The no-clobber check is best-effort, not atomic with other writers.setup --api-id ID --api-hash-stdin avoids putting the API hash in shell history or process arguments.--timeout 30s bounds a daemon RPC after startup, not daemon startup itself.cat emits raw file bytes and does not support JSON. It verifies downloads in a private temporary directory before writing them to stdout; a crash can leave plaintext there.Use Mount in the app, or close the GUI and run tdrive mount. TDrive starts a private localhost WebDAV endpoint and attaches the selected drive to the operating system until it is ejected.
If the selected encrypted drive is locked, the CLI prompts for its existing vault password and retries the mount. Scripts can supply the password through stdin with tdrive mount --password-stdin --non-interactive; mount start does not support JSON mode.
T:. Windows WebDAV has a default 50,000,000-byte file-size limit that must be changed in the OS for larger files.gvfs-backends on Debian or Ubuntu.Personal-drive mounts support create, replace, rename, move, and delete. Encrypted writes are staged as ciphertext before Telegram commit. Encrypted uploads require a known content length. Shared-drive mounts remain read-only.
Linux mount behavior depends on the desktop's GIO/GVfs integration and should still be treated as beta.
tdrive cat may stage decrypted content in a temporary file before writing it to standard output.Use the Go version declared in go.mod, Node.js 22 with npm, and the pinned Wails v3 CLI below. Install the native dependencies for your platform first; see the build guide and Wails installation guide.
Run these commands from the repository root:
go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-beta.22
# Dependencies and embedded frontend
npm --prefix frontend ci
npm --prefix frontend run build
# CLI
go build -o build/bin/tdrive ./cmd/tdrive
# Desktop development/build
wails3 dev
wails3 task build # binary only, into build/bin
wails3 task package # + platform packaging (.app / NSIS installer / AppImage)
Android and iOS build commands are in the mobile build guide.
After installing the platform build dependencies, run from the repository root:
go test ./...
go vet ./...
npm --prefix frontend run typecheck
npm --prefix frontend run lint
npm --prefix frontend run test:coverage
npm --prefix frontend run build
bash scripts/test-android-version-code.sh
bash scripts/test-android-signer-digests.sh
On Linux, add -tags=gtk3 to Go build, test, and vet commands to match this project's desktop configuration.
Browser tests run from frontend/:
npx playwright install --with-deps chromium webkit
npm run test:e2e
npx playwright test --config playwright.gallery-webkit.config.ts
Native playback packaging is handled by the scripts under scripts/ and the release workflow. When a bundled runtime is unavailable, TDrive can fall back to mpv from PATH; TDRIVE_MPV_BIN overrides that binary.
Desktop persistent files live in the operating system's user-config directory:
~/Library/Application Support/TDrive/~/.config/TDrive/%AppData%\TDrive\Important files include:
imp_config.json: Telegram API ID and hashsession.json: Telegram login sessionconfig.json: personal-drive channel configurationtdrive.db: local projection, sync log, and encryption metadataphoto-backup.db: backup sources, settings, and queuecli.json: CLI drive and working-directory statedaemon.log: CLI daemon logbackend.lock: prevents concurrent GUI and daemon ownershipThe Unix daemon socket is runtime-only under $XDG_RUNTIME_DIR or /tmp/tdrive-<uid>. Windows uses a per-user named pipe restricted to the current Windows SID.
Mobile builds use app-private storage. Configuration and session files contain sensitive account data; do not attach them to public bug reports.
TDrive began as my first Go project and a way to learn Go, Wails, and Telegram APIs. I used AI to help with frontend styling, planning, and some implementation areas where the Telegram documentation was difficult, while focusing my learning on the Go and Telegram side.
For bug reports, include the TDrive version, operating system, expected behavior, and steps to reproduce. Redact API hashes, passwords, session data, private filenames, and invite links from logs and screenshots.
TDrive is licensed under the MIT License.
Go
96.6%
Shell
1.6%
Go + Wails desktop app I built as my first Go project. It uses a private Telegram channel like a “cloud drive” so you can upload, download, and organize files into folders (for learning/education only).
Go
97
310 commits
updated Oct 3, 2026
Your files, organized around your own Telegram account.
A desktop and mobile file manager with media streaming, encrypted photo and video backup, shared drives, and a scriptable CLI.
Download TDrive · Get started · Features · Photo backup · CLI and agents · Build
[!IMPORTANT] TDrive is an independent educational project and is not affiliated with Telegram. Use it responsibly and follow Telegram's terms. Do not treat Telegram or TDrive as the only copy of irreplaceable data.
Download packages from GitHub Releases, not from unofficial mirrors. Open the release's Assets list and choose the package for your device.
| Platform | App package | CLI | Distribution |
|---|---|---|---|
| macOS Apple silicon | *-macos-arm64.zip | *-darwin-arm64-cli.tar.gz | GitHub Releases |
| Windows amd64 | *-windows-amd64-setup.exe or portable .zip | *-windows-amd64-cli.zip | GitHub Releases |
| Linux amd64 | *-x86_64.AppImage | *-linux-amd64-cli.tar.gz | GitHub Releases |
| Android ARM64 | *-android-arm64.apk when published | Not available | GitHub only, not the Play Store |
| iOS | No public download yet | Not available | Source builds only |
The Windows installer runs per user without administrator rights. The portable zip contains the same application without the installer.
Linux AppImages require glibc 2.35 or newer (Ubuntu 22.04 or a compatible distribution). For the first upgrade from v2.0.0 or earlier, download TDrive-v2.0.1-x86_64.AppImage manually, make it executable, and launch it. Earlier versions' in-app updater expects the old filename; v2.0.1 recognizes the new name for subsequent updates. Your existing account and drive data are retained.
Updates are manual: download a newer APK from GitHub and install it over the existing release build. Official release updates use the same signing certificate.
TDrive is not notarized by Apple. If macOS cannot verify the developer, proceed only after checking that you downloaded the app from this repository's releases and trust it.
This adds an exception for the app; it does not require disabling Gatekeeper. See Apple's explanation of these warnings.
TDrive stores your API credentials locally; it does not ship a shared project-wide Telegram credential. Keep the API hash, login session, and vault password private.
Already use TDrive on another device? Sign into the same Telegram account and select the same personal-drive channel. If encryption is configured, unlock it with your existing vault password. A new device does not need a new encryption password for that drive.
![]() | ![]() | ![]() |
| Gallery | File viewer | Encrypted file unlock |
.zip, .tar, and .tar.gz archives, with optional extraction.Open Photo & video backup from the desktop account menu or the Android Account tab. Choose your folders and start backup. Files go into Photo backup / <device> / <source> inside My Drive, under your selected destination parent if configured.
| Device | Sources |
|---|---|
| Desktop | Selected local folders, including subfolders |
| Android | Permitted photos and videos in selected folders on device storage |
| iOS source build | Backup source selection is not available |
Backup is encrypted and personal-drive-only. It reuses your drive's existing vault: unlock it with the same password used on your other devices. If that drive has no vault yet, TDrive asks you to set one up.
Backup never deletes device originals. Removing a source does not delete files already uploaded to TDrive. Pause, queue state, and confirmed uploads are remembered across restarts.
Mobile permissions determine which media TDrive can see. Discovery runs while the app is active; an in-flight upload may continue with a native background allowance. Backup is not a continuously scheduled uploader and cannot continue after the app process is terminated. Keep the app open for the initial backup of a large library.
For the detailed recovery, staging, and platform limits, see the backup documentation.
TDX1|... metadata messages.[!WARNING]
TDX1|...messages are part of the drive history. Do not edit or delete them in the Telegram app; doing so can damage or desynchronize the projected filesystem.
Manual uploads on My Drive can use client-side XChaCha20-Poly1305 encryption. Photo and video backup always uses encryption. TDrive encrypts file contents before sending them to Telegram and decrypts them after the correct vault password is entered.
Important limits:
Shared drives support file and folder upload, download, rename, move, and delete. Members can see who uploaded a file and can organize the shared namespace.
Invite links should be treated like passwords. Use approval-required links when membership needs review, and revoke links that should no longer grant access.
Deleting a shared folder also deletes its contained files after confirmation. Shared drives mount read-only even though they remain writable through the app and CLI.
The CLI is available for macOS, Linux, and Windows amd64. Most commands automatically start a local daemon that keeps the Telegram connection, sync state, and unlocked vault key warm between commands.
The GUI and CLI daemon cannot use the same backend state simultaneously. Close the GUI before using CLI commands; the CLI reports this conflict instead of starting a second backend.
Download and extract the CLI archive for your platform. Open a terminal in the extracted TDrive-...-cli directory, then run:
./install-cli.sh
Reload the shell if the installer updated its configuration, then set up and log in:
tdrive version
tdrive setup --api-id YOUR_ID --api-hash-stdin
tdrive login +15551234567
tdrive whoami
tdrive ls
Extract the *-windows-amd64-cli.zip release asset and run tdrive.exe from PowerShell:
.\tdrive.exe setup --api-id YOUR_ID --api-hash-stdin
.\tdrive.exe login +15551234567
.\tdrive.exe whoami
tdrive drives
tdrive drive use <name|id>
tdrive ls -l
tdrive mkdir -p /Photos
tdrive put photo.jpg /Photos/
tdrive get /Photos/photo.jpg .
tdrive mkdir -p /Imports
tdrive put --extract archive.zip /Imports/
tdrive mv /old /new
tdrive rm -r /folder
tdrive sync
tdrive rebuild
tdrive unlock
tdrive mount
tdrive mount status
tdrive mount stop
Folder and archive imports require the destination folder to exist. Single-file uploads can create or rename the final file path.
Shared-drive commands include drive create, drive link, drive join, drive requests, drive approve, drive deny, and drive leave. Run tdrive help or tdrive <command> --help for the complete command reference.
Start with the offline version and command manifest. Neither needs a daemon or Telegram connection. tdrive commands --json is the authoritative list of JSON-supported commands, flags, and mutation behavior.
tdrive version --json
tdrive commands --json
After interactive setup and login, use JSON output and explicit drive IDs for scripts:
tdrive drives --json
tdrive ls /Photos --drive-id 123456789 --json --non-interactive
tdrive put photo.jpg /Photos/photo.jpg --drive-id 123456789 --json --non-interactive
Use the numeric drive ID returned by tdrive drives --json, replacing the example ID above. Drive-scoped JSON commands require --drive-id and canonical absolute remote paths. They do not change the daemon's shared active drive or working directory, so concurrent scripts can target different drives safely. Human-readable output remains the default; --json (or --output json) is opt-in and rejects unsupported commands.
Each successful JSON command writes one schema-versioned object to stdout (schema_version, ok, command, data), with no progress text. An error writes one object to stderr (schema_version, ok: false, error with code, message, retryable, and optional hint). Branch on error.code, not the message.
| Exit code | Meaning |
|---|---|
0 | Success |
1 | Other operation failure |
2 | Invalid or unsupported command |
3 | Required input or authentication |
4 | Not found |
5 | Conflict or required confirmation |
6 | Timeout or unavailability |
--non-interactive never prompts. Vault unlock requires --password-stdin in JSON or non-interactive mode.rm and rebuild require --yes. Non-interactive text mode also requires it for destructive drive actions and full logout. There is no dry-run option.put accepts one regular local file. JSON get requires an explicit local file path and refuses to overwrite it unless --yes is given. The no-clobber check is best-effort, not atomic with other writers.setup --api-id ID --api-hash-stdin avoids putting the API hash in shell history or process arguments.--timeout 30s bounds a daemon RPC after startup, not daemon startup itself.cat emits raw file bytes and does not support JSON. It verifies downloads in a private temporary directory before writing them to stdout; a crash can leave plaintext there.Use Mount in the app, or close the GUI and run tdrive mount. TDrive starts a private localhost WebDAV endpoint and attaches the selected drive to the operating system until it is ejected.
If the selected encrypted drive is locked, the CLI prompts for its existing vault password and retries the mount. Scripts can supply the password through stdin with tdrive mount --password-stdin --non-interactive; mount start does not support JSON mode.
T:. Windows WebDAV has a default 50,000,000-byte file-size limit that must be changed in the OS for larger files.gvfs-backends on Debian or Ubuntu.Personal-drive mounts support create, replace, rename, move, and delete. Encrypted writes are staged as ciphertext before Telegram commit. Encrypted uploads require a known content length. Shared-drive mounts remain read-only.
Linux mount behavior depends on the desktop's GIO/GVfs integration and should still be treated as beta.
tdrive cat may stage decrypted content in a temporary file before writing it to standard output.Use the Go version declared in go.mod, Node.js 22 with npm, and the pinned Wails v3 CLI below. Install the native dependencies for your platform first; see the build guide and Wails installation guide.
Run these commands from the repository root:
go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-beta.22
# Dependencies and embedded frontend
npm --prefix frontend ci
npm --prefix frontend run build
# CLI
go build -o build/bin/tdrive ./cmd/tdrive
# Desktop development/build
wails3 dev
wails3 task build # binary only, into build/bin
wails3 task package # + platform packaging (.app / NSIS installer / AppImage)
Android and iOS build commands are in the mobile build guide.
After installing the platform build dependencies, run from the repository root:
go test ./...
go vet ./...
npm --prefix frontend run typecheck
npm --prefix frontend run lint
npm --prefix frontend run test:coverage
npm --prefix frontend run build
bash scripts/test-android-version-code.sh
bash scripts/test-android-signer-digests.sh
On Linux, add -tags=gtk3 to Go build, test, and vet commands to match this project's desktop configuration.
Browser tests run from frontend/:
npx playwright install --with-deps chromium webkit
npm run test:e2e
npx playwright test --config playwright.gallery-webkit.config.ts
Native playback packaging is handled by the scripts under scripts/ and the release workflow. When a bundled runtime is unavailable, TDrive can fall back to mpv from PATH; TDRIVE_MPV_BIN overrides that binary.
Desktop persistent files live in the operating system's user-config directory:
~/Library/Application Support/TDrive/~/.config/TDrive/%AppData%\TDrive\Important files include:
imp_config.json: Telegram API ID and hashsession.json: Telegram login sessionconfig.json: personal-drive channel configurationtdrive.db: local projection, sync log, and encryption metadataphoto-backup.db: backup sources, settings, and queuecli.json: CLI drive and working-directory statedaemon.log: CLI daemon logbackend.lock: prevents concurrent GUI and daemon ownershipThe Unix daemon socket is runtime-only under $XDG_RUNTIME_DIR or /tmp/tdrive-<uid>. Windows uses a per-user named pipe restricted to the current Windows SID.
Mobile builds use app-private storage. Configuration and session files contain sensitive account data; do not attach them to public bug reports.
TDrive began as my first Go project and a way to learn Go, Wails, and Telegram APIs. I used AI to help with frontend styling, planning, and some implementation areas where the Telegram documentation was difficult, while focusing my learning on the Go and Telegram side.
For bug reports, include the TDrive version, operating system, expected behavior, and steps to reproduce. Redact API hashes, passwords, session data, private filenames, and invite links from logs and screenshots.
TDrive is licensed under the MIT License.
Go
96.6%
Shell
1.6%