replica-omnisciente/telminal/README.md
Aurelio 56be25c1ed feat(telminal): add hardened Terminal-in-Telegram bot to aurelio
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.
2026-07-15 23:42:31 +01:00

4.4 KiB

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, ^z map 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.log periodically pushes a file.
  • Multi-admin: first admin can !trust/!untrust other users (by reply).
  • Sandboxed cwd: cd is 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).