tgterm
Motivations
Since coding agents started reshaping the way we write code, many developers started to feel the need to access their terminals while away from the keyboard. Providing instructions to agents, for them to continue the development, is now possible without typing much, just observing, evaluating, and sending further instructions: something that can be done via a phone keyboard.
The usual setup now is to: setup some kind of SSH tunneling, or VPN between the computer and the phone. Starting agent coding sessions via Tmux, and then using Termux or a plain mobile SSH client in order to access remote sessions. But, I consider Telegram a better way to access my sessions for the following reasons:
- Terminal is already on your phone, and you get actual screenshots o…
tgterm
Motivations
Since coding agents started reshaping the way we write code, many developers started to feel the need to access their terminals while away from the keyboard. Providing instructions to agents, for them to continue the development, is now possible without typing much, just observing, evaluating, and sending further instructions: something that can be done via a phone keyboard.
The usual setup now is to: setup some kind of SSH tunneling, or VPN between the computer and the phone. Starting agent coding sessions via Tmux, and then using Termux or a plain mobile SSH client in order to access remote sessions. But, I consider Telegram a better way to access my sessions for the following reasons:
- Terminal is already on your phone, and you get actual screenshots of your terminals, that you can update with a click on a button. You can just type in just writing a Telegram message, that is what you need to work with Claude Code, Codex and other agents.
- I don’t need to remember to start sessions multiplexed with tmux / screen. For instance, I don’t use multiplexing normally, why caring?
- Setup burden of SSH tunnels, VPNs, ...
- SSH does not work in a very important case: if the development at hand involves inspecting graphical output of any kind. This is extremely important in certain setups: for instance, in the development of flux2.c, for me it makes a lot of a difference.
So I built this project, that allows to control many terminal sessions using Telegram.
This is how it works:
- You talk with a Telegram bot, that you create only for yourself.
- After you setup your TOTP, you send the bot the first message, and you become its owner. It will only accept queries from you (your Telegram ID) and will require you to authenticat with an OTP for the first time, and again after a timeout.
- At this point, you can ask for the list of terminal windows in your system with
.list, connect to one of them with (for instance).2, then you can send any text that will be "typed" in the window, like if you are still at your computer. You have modifiers, ways to sendESC, and so forth, so you can do many things, like changing the visible tab.
Important: this program only works on macOS for now.
First run
Please note in advance that the program requires two system permissions to function:
- Screen Recording — needed to capture terminal window screenshots.
- Accessibility — needed to inject keystrokes and raise windows.
MacOS will prompt you to grant these on first use. If screenshots or keystrokes silently fail, check System Settings → Privacy & Security.
To setup the project:
- Create a Telegram bot via @BotFather and get the API key.
- Install
libcurlandlibsqlite3. The project also uses my ownbotlibbut it is included directly into the project, so no need to install anything. - Build with
makeand run:
./tgterm --apikey <your-api-key>
- On the first run, the bot will display a QR code and a text secret on your terminal:
=== TOTP Setup ===
Scan this QR code with Google Authenticator:
▄▄▄▄▄▄▄ ▄▄ ▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄
█ ▄▄▄ █ █▄▄█▀▀ █▀▄▄▄▄▀▄█▀ █ ▄▄▄ █
...
Or enter this secret manually: KAPLXWZSYXOR64Y49HG75IIFXHSQLHTY
==================
Open Google Authenticator (or any TOTP app), scan the QR code or type the secret manually. This is the only time the secret is shown — you won’t see it again on subsequent runs.
Send any message to your bot on Telegram (e.g. /start or just hello). The first user to message becomes the owner. The bot will only accept messages from you from now on.
1.
The bot will reply asking for your OTP code. Type the 6-digit code from your authenticator app to unlock it.
You’re ready. Type .list to see your terminal windows and .help for the full command reference.
Usage
Commands
.list— List available terminal windows..1,.2, ... — Connect to a window by its number..help— Show the help message..otptimeout <seconds>— Set the OTP inactivity timeout (range: 30–28800 seconds). Default is 300 (5 minutes).
Sending keystrokes
Once connected to a window, any text you send is typed into it as keystrokes. A newline (Enter) is automatically appended at the end.
Suppressing the newline: End your message with 💜 to prevent the automatic Enter at the end. This is useful for partially typing a command or entering text without submitting it.
Modifier emojis: The .help message includes modifier emojis formatted in backticks so that you can tap to copy them on your phone. Paste a modifier before a character to send that key combination:
- ❤️ — Ctrl (e.g.
❤️csends Ctrl+C) - 💙 — Alt
- 💚 — Cmd
- 💛 — ESC (sends Escape immediately, no following key needed)
- 🧡 — Enter (sends Enter at that position, useful for multi-line input)
Modifiers can be combined: ❤️💙x sends Ctrl+Alt+X. A single modified keystroke (like ❤️c) will not have an automatic newline appended.
Escape sequences: \n sends Enter, \t sends Tab, \\ sends a literal backslash.
Screenshots
After every keystroke message, the bot waits briefly for the terminal to update and then sends back a screenshot of the connected window. The screenshot includes a 🔄 Refresh button that you can tap to get an updated screenshot without sending any keystrokes.
Security
This tool allows remote control of terminal windows via Telegram. Given the sensitivity of this capability, multiple layers of security are in place:
Ownership. The first Telegram user to message the bot becomes its owner. All subsequent messages from other users are silently ignored. The owner’s user ID is stored persistently in the database.
TOTP authentication. On first startup, the bot generates a TOTP shared secret and displays a QR code on the terminal for scanning with Google Authenticator (or any TOTP app). After a configurable period of inactivity (default 5 minutes), the bot locks and requires the owner to enter a valid one-time password before accepting further commands. This protects against scenarios where an attacker gains access to the owner’s Telegram account. The timeout is configurable via .otptimeout.
Terminal-only window access. By default, the bot only lists and connects to known terminal applications (Terminal, iTerm2, Ghostty, kitty, Alacritty, etc.). This limits the attack surface compared to allowing control of arbitrary windows. Note however that this is a convenience restriction, not a real security boundary: anyone with control of a terminal can trivially escape to full system access.
Resetting authentication. To reset everything (owner, TOTP secret, all settings), delete the mybot.sqlite file and restart the bot. A new TOTP secret will be generated and a new QR code displayed.
Disabling TOTP. If you don’t want OTP authentication (not recommended), run with --use-weak-security. The bot will still enforce ownership but will not require OTP codes.
Limitations
- Deprecated macOS APIs. The project uses older Core Graphics and Process Manager APIs for screenshot capture and window management. These produce compiler warnings on macOS 14+ but still work correctly, and provide good compatibility with older macOS versions. They will be replaced if and when Apple removes them.
- UTF-8 keystrokes. Non-ASCII text (beyond the special emoji modifiers) is not handled correctly when sending keystrokes. Only ASCII characters are reliably injected.
Credits
- QR Code generator library by Project Nayuki — MIT license.
- SHA-1 implementation by Steve Reid — 100% public domain.