Skip to content

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 q or Ctrl-C to exit cleanly; state is flushed to disk

Architecture and components

Config loader (config.rs)

  • *Parses config.toml using serde + 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 tokio task per endpoint
  • *Each task loops: fires an HTTP GET via reqwest, evaluates the response against expected_status, updates the shared state, and sleeps for interval_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.json on startup and shutdown (and periodically while running)

TUI (ui.rs)

Built with ratatui and crossterm.

The interface uses a two-section layout:

SectionDescription
Dashboard tableOne row per endpoint โ€” name, URL, status badge (color-coded), HTTP code, uptime %, response time in ms, last-checked time
Events logScrolling 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

KeyScopeDescription
state_fileglobalPath to the JSON file for state persistence
telegram_bot_tokenglobalTelegram bot token from @BotFather
telegram_chat_idglobal / endpointTarget channel or chat
webhook_urlglobal / endpointSlack-compatible webhook URL
nameendpointDisplay name in the dashboard
urlendpointURL to poll
interval_secsendpointPolling interval in seconds
expected_statusendpointExact HTTP status for UP; omit for any 2xx
timeout_secsendpointRequest timeout in seconds (default: 10)

Tech stack

LayerLibrary
Async runtimetokio
HTTP clientreqwest
TUIratatui + crossterm
Config / stateserde + toml + serde_json
Timechrono
CLI argsclap
Error handlinganyhow

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