Production-ready Telegram bot for server monitoring and management, written in pure C.
- CPU load (1 / 5 / 15 min)
- Memory usage (MB + %)
- Disk usage (GB + %)
- Uptime
- Active users
- Check system services status via
systemctl - Start, stop, restart individual services via
/service <alias> <action>
Supported actions: status, start, stop, restart
Supports the following services:
- SSH
- Shadowsocks
- MTG MTPROTO proxy for Telegram
by Sergei Arkhipov aka 9seconds
https://github.com/9seconds/mtg
-
Clean formatted output with status indicators:
- π’ UP
- π΄ DOWN
- π‘ FAIL / STARTING
- βͺ UNKNOWN
- View service logs via
journalctl(/logs) - Semantic log filtering:
error,auth,brute,ip,session,pam - Multi-keyword AND filtering (e.g.
/logs ssh error) - Inspect active user sessions
- Configurable line count (default 30, max 200)
Built-in metrics collected since last bot start:
Commands:
- Total commands processed
- Per-command counters (
/start,/help,/logs,/fail2ban,/reboot,/restart,/services,/users,/health)
Errors:
- Unauthorized access attempts
- Polling timeouts
- Exec failures (sudo, systemctl, journalctl)
Performance:
- Average / max response time (ms)
- Poll cycles count
- Telegram API calls (total / failed)
How to check: /health β compact view with system status + bot metrics
On shutdown, full metrics summary is written to the log:
[SYS] Commands: 42 (start=1 help=2 logs=15 ...) | Errors: unauthorized=0 timeout=0 exec=0 | Response: avg=142ms max=570ms
- π Single allowed chat_id β only one Telegram user can control the bot
- π« All unauthorized access attempts are logged:
poll=01a3 req=2212 ACCESS CHECK: chat_id=123456789 cmd=/start result=DENIED - π Two-step confirmation for dangerous commands (
/reboot,/restart,/service start|stop|restart)- TOTP mode (when
TOTP_SECRETis configured): time-based one-time code from Google Authenticator, Aegis, Authy or any RFC 6238 app - Token mode (fallback): stateless time-based token with configurable TTL
- Bruteforce protection (blocks after 5 failed attempts)
- Replay protection (single-use)
- TOTP mode (when
- π§ͺ Input validation:
- Message length limit
- IP address validation (Fail2Ban)
- Character whitelist for command arguments
- Check jail status (
/fail2ban status,/fail2ban status sshd) - Ban / unban IP addresses
- Secure wrapper execution via
f2b-wrapper - Full audit logging via syslog (
LOG_AUTHfacility)
/restartβ restart the bot process (systemctl restart tg-bot)/rebootβ reboot the server- Both require two-step confirmation (TOTP or token)
- Tracks who requested the operation
- Long polling (no webhooks)
- Fork isolation for libcurl β network hangs cannot block the main process
- Dynamic pipe buffer β handles large Telegram responses (100+ updates) without truncation
- Offset persistence with atomic write (crash recovery, no duplicate messages)
- Runtime duplicate detection
- MarkdownV2 formatting with automatic escaping
- Automatic message truncation (4096 char limit)
- Safe JSON parsing β no crashes on malformed Telegram responses
- NULL checks everywhere
- Deadline-based curl timeouts (no busy-wait)
- Zero zombie processes β blocking waitpid after pipe EOF
- Early log buffer β startup messages are captured before log file opens
- Graceful shutdown on SIGTERM and on consecutive polling errors:
- Saves update offset to disk
- Flushes logs
- Syncs filesystem
- systemd watchdog support (
Type=notify,WatchdogSec=60) - Log rotation support via
SIGUSR1 - Main loop iteration timing β warns if iteration exceeds 35s
- Levels:
DEBUG/INFO/WARN/ERROR - Millisecond-precision timestamps (single
clock_gettimecall β no race condition) - Complete log from process start β early messages buffered and flushed to file on open
- Mirror to stderr when running interactively (isatty detection)
- No stderr duplication when running as systemd service
- Thread-safe writes via mutex
- Safe formatting β no crashes on bad input
Dangerous commands (/reboot, /restart, /service start|stop|restart) support TOTP 2FA
compatible with Google Authenticator, Aegis, Authy and any RFC 6238 application.
1. Generate a secret:
python3 -c "import base64, os; print(base64.b32encode(os.urandom(20)).decode())"
# or:
openssl rand 20 | base322. Add to config file and enable setup mode:
TOTP_SECRET=YOUR_BASE32_SECRET_HERE
TOTP_SETUP=enabled
3. Reload config:
kill -HUP $(pidof tg-bot)4. Get import link for your app:
/totp_setup
Copy the otpauth:// URI into Aegis, Google Authenticator or Authy.
5. After adding to your app β disable setup mode:
TOTP_SETUP=disabled
Then reload config again. This prevents the secret from being exposed if someone gains access to the bot.
you: /reboot
bot: π Confirm: /reboot
Enter the code from your authenticator app:
/confirm <code>
you: /confirm 428798
bot: β»οΈ Rebooting system...
When TOTP_SECRET is not configured, the bot falls back to the classic
stateless token flow automatically β no configuration change needed.
TOTP_SETUP=disabled (default) prevents /totp_setup from showing the
secret after initial setup. Even if someone gains access to the bot,
they cannot retrieve the TOTP secret via chat.
tg-bot --config /etc/tg-bot/config.confcp config/config.example.conf config/config.conf
# edit config.conf β set TOKEN and CHAT_ID-c,--config <path>β config file path-h,--helpβ show help-v,--versionβ show version with build info
General
/start β welcome message with system summary + selfcheck (RSS, log file)
/help β list all commands
System info
/status β detailed system status (CPU, memory, disk, uptime)
/health β compact health check + bot metrics (with used/total MB and GB)
/about β bot version, build info, library versions
/ping β latency test (processing time, inbound, RTT)
/logstat β bot log file statistics (SSE4.2 accelerated)
Services
/services β status of all monitored services
/service ssh status β status of a single service
/service ssh restart β restart a service (requires confirmation)
/service shadowsocks stop β stop a service (requires confirmation)
/users β active login sessions
/logs ssh β last 30 lines from ssh journal
/logs ssh 50 β last 50 lines
/logs ssh error β filtered by "error" (semantic)
/logs ssh 100 brute β last 100 lines filtered for brute-force patterns
Security
/fail2ban status β show all jails
/fail2ban status sshd β show sshd jail
/fail2ban ban 1.2.3.4 β ban IP
/fail2ban unban 1.2.3.4 β unban IP
System control (two-step confirmation required)
/reboot β reboot the server
/restart β restart the bot process
/totp_setup β show TOTP setup info and import link
/confirm β confirm a pending operation (TOTP code or token)
Modular C design β each module has a single responsibility:
main.cβ orchestration, main event loop, signal handlinglifecycle.cβ process lifecycle (signals, shutdown, reboot, restart)telegram_poll.cβ long polling with fork() isolation for libcurl, RSS samplingtelegram_http.cβ low-level HTTP via libcurltelegram_parser.cβ JSON parsing, markdown escapingtelegram_offset.cβ update offset persistencecommands.cβ command dispatcher, two-step confirmation logicsecurity.cβ access control, rate limiting, token validationtotp.cβ TOTP implementation (RFC 6238, HMAC-SHA1 via OpenSSL)config.cβ configuration file parsing and reloadlogger.cβ thread-safe logging with early bufferdiagnostics.cβ runtime diagnostics (loop timing)services_config.cβ shared service definitions (single source of truth)services.cβ systemd service status queries and controlsystem.cβ system metrics (CPU, memory, disk, uptime)exec.cβ external command execution with deadline timeoutlogs.c+logs_filter.cβ journalctl integration and semantic filteringusers.cβ active session enumeration via utmpmetrics.cβ bot usage statisticsutils.cβ shared helpers
- Offset saved atomically via temp file + rename (no corruption on crash)
pipe2(O_CLOEXEC)β no fd leaks into child processes- Deadline-based
select()β total timeout is always exactlytimeout_ms - Blocking
waitpid()after pipe EOF β no zombie window - Dynamic heap buffer for Telegram API responses β no fixed cap, handles 100+ updates
getrandom(2)for token salt β cryptographically unpredictable- All timeout constants in
telegram_timeouts.hwith_Static_assertchain - Config reload on
SIGHUPwithout restart - Log reopen on
SIGUSR1for logrotate integration - TOTP verification uses Β±1 step window to compensate for clock skew
- TOKEN and TOTP_SECRET never appear in log files β masked even at DEBUG level
- f2b-wrapper logs all operations to syslog (
LOG_AUTH) with PID
- Bot responds only to one chat_id β hardcoded in config
- All unauthorized attempts are logged with chat_id
- No shell injection β IP validation via
inet_pton, service names via character whitelist - No direct root commands β all privileged operations via sudo whitelist
- Token salt generated via
getrandom(2)at startup β unpredictable across restarts - TOTP secret never logged β not even at DEBUG level
TOTP_SETUP=disabledby default β secret not exposed via/totp_setupafter setup- f2b-wrapper rejects private/loopback IP addresses for ban/unban operations
Primarily tested on:
- OS: Ubuntu 18.04.6 LTS x86_64
- Compiler: GCC 7.5.0
- Libraries (all statically linked):
- π libcurl 8.20.0 β HTTP client (curl license)
- π OpenSSL 3.0.20 β TLS/SSL (Apache 2.0)
- π§ c-ares 1.34.6 β async DNS resolver (MIT)
- π cJSON 1.7.19 β JSON parser (MIT)
β‘ The binary is fully self-contained (~5.5 MB stripped). Only
libcis required at runtime.
No dependencies required on the target system:
chmod +x tg-bot
sudo cp tg-bot /usr/local/bin/Built in Ubuntu 18.04 environment using Docker for reproducibility.
Linking model:
- Fully static: libcurl, OpenSSL, c-ares, cJSON compiled from source
- Only
libc(glibc) remains dynamically linked
f2b-wrapper β required only for /fail2ban commands.
Source: tools/f2b-wrapper.c
The
f2b-wrapperbinary must be built on the target system.
Using a binary compiled elsewhere may causeGLIBC_X.Y not founderrors.
If you see this at startup:
[ERROR] [SYS] fail2ban-wrapper: FAIL (EXEC_FAILED)
Rebuild f2b-wrapper locally on the target machine.
/service <alias> <action>β start, stop, restart individual services via Telegram
- Log filtering by time (
--since 1h) /dfβ disk usage quick view/topβ top processes by CPU/RAM/fail2ban status <jail>β detailed jail statistics/ssh keysβ show authorized SSH keys- Scheduled commands (
/reboot in 5) - Alerts β bot notifies on high CPU/RAM load
/backupβ trigger backup script via Telegram
- Notification on bot stop
- Unit tests (TOTP RFC 6238 vectors, security.c, logs_filter.c)
- Prometheus metrics export (requires server with >2GB RAM)
Built with focus on reliability, control and independence.
Inspired by the idea that infrastructure should remain in your hands.
Stay independent. π
MIT License Β© 2026 ironmist45