Tortuga ๐ดโโ ๏ธ
"Make way! Make way for Tortuga!" โ Captain Barbossa
A terminal-based HTTP endpoint monitor written in Rust. Tortuga watches your HTTP services, renders a live dashboard in the terminal, and fires instant alerts via Telegram and Slack/Discord webhooks whenever a service goes up or down.
Why I built Tortuga
I wanted a lightweight, self-hosted way to keep an eye on a handful of HTTP services โ APIs, health endpoints, personal projects โ without spinning up a full observability stack or relying on a paid SaaS tool.
The result is a single Rust binary: drop in a config.toml, run it, and you get a live terminal dashboard plus push notifications the moment something changes status.
Repository: github.com/gianlucarea/tortuga
Dashboard
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Tortuga โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Name URL Status HTTP Uptime ms โ
โ httpbin OK https://httpbin.org/status/200 UP 200 100% 87 โ
โ httpbin 404 https://httpbin.org/status/404 DOWN 404 0% 102 โ
โ Example.com https://example.com UP 200 99% 210 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Events โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 16:34:01 [httpbin OK] Unknown โ UP (HTTP 200) โ
โ 16:34:02 [httpbin 404] Unknown โ DOWN (HTTP 404) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Green = UP ยท Red = DOWN ยท Yellow = Unknown / timeout
Features
- *Live TUI dashboard โ real-time ratatui table refreshed every second, showing status, HTTP code, uptime %, response time, and last-checked timestamp
- *Concurrent monitoring โ each endpoint runs in its own async task (tokio) with an independent polling interval and timeout
- *Telegram alerts โ instant push notifications via the Telegram Bot API to a channel or chat
- *Webhook alerts โ Slack-compatible JSON POST to any webhook URL (Slack, Discord, etc.)
- *State persistence โ uptime counters and last status survive restarts via a local
state.json - *Graceful shutdown โ press
qorCtrl-Cto exit cleanly; state is flushed to disk
Architecture and components
Config loader (config.rs)
- *Parses
config.tomlusingserde+toml - *Supports global defaults (Telegram token, webhook URL, state file path) with per-endpoint overrides
- *Each endpoint declares a name, URL, polling interval, optional expected status code, and timeout
Monitor (monitor.rs)
- *Spawns one
tokiotask per endpoint - *Each task loops: fires an HTTP
GETviareqwest, evaluates the response againstexpected_status, updates the shared state, and sleeps forinterval_secs - *Status transitions (UP โ DOWN, DOWN โ UP, Unknown โ *) trigger alert dispatch
Alert dispatcher (alert.rs)
- *Telegram: sends a formatted message via
https://api.telegram.org/bot<token>/sendMessage - *Webhook: POSTs a Slack-compatible JSON payload to the configured URL
- *Both channels are optional; per-endpoint overrides take precedence over global config
State (state.rs)
- *Holds a
HashMap<endpoint_name, EndpointState>with total checks, up-count, last HTTP code, and last checked time - *Serialized to / deserialized from
state.jsonon startup and shutdown (and periodically while running)
TUI (ui.rs)
Built with ratatui and crossterm.
The interface uses a two-section layout:
| Section | Description |
|---|---|
| Dashboard table | One row per endpoint โ name, URL, status badge (color-coded), HTTP code, uptime %, response time in ms, last-checked time |
| Events log | Scrolling list of status-change events with timestamps |
Configuration
[global]
state_file = "state.json"
telegram_bot_token = "123456:ABC-DEF..."
telegram_chat_id = "@mychannel"
# webhook_url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
[[endpoints]]
name = "My API"
url = "https://api.example.com/health"
interval_secs = 30
expected_status = 200
timeout_secs = 10
[[endpoints]]
name = "Example.com"
url = "https://example.com"
interval_secs = 60
Configuration reference
| Key | Scope | Description |
|---|---|---|
state_file | global | Path to the JSON file for state persistence |
telegram_bot_token | global | Telegram bot token from @BotFather |
telegram_chat_id | global / endpoint | Target channel or chat |
webhook_url | global / endpoint | Slack-compatible webhook URL |
name | endpoint | Display name in the dashboard |
url | endpoint | URL to poll |
interval_secs | endpoint | Polling interval in seconds |
expected_status | endpoint | Exact HTTP status for UP; omit for any 2xx |
timeout_secs | endpoint | Request timeout in seconds (default: 10) |
Tech stack
| Layer | Library |
|---|---|
| Async runtime | tokio |
| HTTP client | reqwest |
| TUI | ratatui + crossterm |
| Config / state | serde + toml + serde_json |
| Time | chrono |
| CLI args | clap |
| Error handling | anyhow |
Usage
# Clone and build
git clone https://github.com/gianlucarea/tortuga
cd tortuga
cargo build --release
# Run with default config.toml
./target/release/tortuga
# Specify a custom config
./target/release/tortuga --config /etc/tortuga/prod.toml