Port fristhon/telminal (MIT) into the aurelio monorepo as a self-hosted Telegram shell bot for the fleet gateway (CT-217). - telminal/ package: config (env + state file, refuses empty admins), core orchestrator (event handlers, router, watchers, interactive mode, file up/download, xterm.js image render), process (pexpect PTY + streaming + inline control buttons), telegram (Telethon wrapper, swappable for tests), cli (entry point reading TELEGRAM_* env), utils, values. - aurelio hardening vs upstream: no first-run random token auth (explicit admin allowlist required); cd sandbox validated against working root; secrets from env mirroring the CT-217 gateway .env. - 35 real tests (pty capture/control-char, router, watchers, perms, sandbox, fake-client orchestration) -- all green. - deployment: systemd/telminal.service, .env.example entries, README, AGENTS.md. Verified: pytest 35 passed; CLI refuses start with missing env / no admins. |
||
|---|---|---|
| .. | ||
| systemd | ||
| telminal | ||
| tests | ||
| .gitignore | ||
| AGENTS.md | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| requirements-dev.txt | ||
| requirements.txt | ||
telminal — A Terminal in Telegram (aurelio port)
A self-hosted Telegram bot that gives you a sandboxed shell on the host from Telegram. It is a clean re-implementation of fristhon/telminal (MIT), ported into the Réplica Omnisciente / Portugal Futurista monorepo with aurelio conventions.
⚠️ Security: telminal executes shell commands on the host it runs on. It must only ever be deployed on a host you control (the fleet's CT-217 gateway) with an explicit admin allowlist. It refuses to start without configured admins.
Features
- Run any shell command via a PTY (
/bin/bash -c) and stream the output back to Telegram, with inline control buttons:- 💡 Info — process status (PID / runtime / last update)
- ↩️ Enter — send a newline / Enter to the process
- Interactive mode — talk to a running process (each message = input);
^c,^d,^zmap to Ctrl-C / Ctrl-D / Ctrl-Z - 🛑 Terminate — kill the process
- 🌐 HTML — receive the full output as a rendered HTML file
- Optional image output: render the terminal with xterm.js and send a
screenshot (
/image_on,/image_off). - File download from server:
!get <path>or the inline file picker. - File upload to server: send a file to the chat and confirm save.
- Watcher tasks:
!watch 50s telminal.logperiodically pushes a file. - Multi-admin: first admin can
!trust/!untrustother users (by reply). - Sandboxed cwd:
cdis validated and cannot escape the configured working directory root.
Architecture
telminal/
├── pyproject.toml # packaging + console script `telminal`
├── requirements.txt
├── systemd/telminal.service
├── telminal/
│ ├── __init__.py
│ ├── cli.py # entry point: reads env, builds Config, runs bot
│ ├── config.py # Config (env + state file), refuses empty admins
│ ├── core.py # Telminal orchestrator (event handlers, router)
│ ├── process.py # TProcess: pexpect PTY + streaming + buttons
│ ├── telegram.py # thin Telethon wrapper (swappable for tests)
│ ├── utils.py # HTML template + helpers
│ └── values.py # message templates + regexes
└── tests/ # real tests (pty, router, watchers, perms, fake tg)
The orchestrator depends on Telegram only through Telegram (telegram.py), so
the full routing / permission / watcher logic is exercised in tests with a fake
client — no network required.
Configuration
All secrets come from the environment (matches the CT-217 gateway .env):
| Variable | Required | Meaning |
|---|---|---|
TELEGRAM_API_ID |
yes | Telegram API id |
TELEGRAM_API_HASH |
yes | Telegram API hash |
TELEGRAM_TOKEN |
yes | Bot token from @BotFather |
TELEGRAM_ADMINS |
yes | Comma-separated numeric user ids (allowlist) |
TELEGRAM_WORKING_DIR |
no | Sandbox root for cd / file ops (default: cwd) |
The admin list and working directory also persist to config.json next to the
package so !trust/cd survive restarts.
Create your bot + API credentials at:
Set BotFather commands:
image_off - Just text output
image_on - Text and image output
tasks - List of active tasks
interacive_mode - Interact with last process
normal_mode - Process creation mode
Running locally
python3.11 -m venv .venv
. .venv/bin/activate
pip install -e ".[dev]"
export TELEGRAM_API_ID=... TELEGRAM_API_HASH=... TELEGRAM_TOKEN=...
export TELEGRAM_ADMINS=123456789
python -m telminal
Install the optional image-rendering backend with pip install ".[image]".
Deploying on CT-217 (fleet gateway)
# on the gateway host (CT 217):
sudo cp systemd/telminal.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now telminal
journalctl -u telminal -f
The unit sources secrets from /home/hermes/.hermes/.env and pins the working
directory to /home/hermes/telminal-workspace.
Tests
. .venv/bin/activate
pytest -q
Covers: PTY spawn + output capture, control-char input, watcher regex parsing, command routing, permission gating, cwd sandboxing, and the full message orchestration driven by a fake Telegram client (no network).