Cross-platform process monitor with Telegram notifications and remote control
Project description
qara is a lightweight daemon that monitors processes and sends real-time notifications to Telegram. Start a long-running job, close your laptop, and stay in control from your phone.
Built for ML practitioners who run multi-hour training jobs, but works with any process.
qara run python train.py --name "gpt-finetune"
# => Telegram: "Process 'gpt-finetune' started (PID 41592)"
# => ... hours later ...
# => Telegram: "Process 'gpt-finetune' finished (exit 0, 3h 42m)"
Features
- Spawn or attach — start a new process with
qara runor monitor an existing one withqara attach <pid> - Telegram notifications — get notified on start, finish, and crash with configurable stdout tail
- Remote control — send
/status,/kill,/history,/logsfrom Telegram - Plugin system — extend with plugins via entry points (e.g.
qara-mlfor GPU metrics and loss tracking) - Daemon mode — runs as a user-level service (systemd, launchd) — no root required
- JSON output —
--format jsononstatusandhistoryfor scripting
Quickstart
Install
pip install qara
# Or with uv (recommended)
uv pip install qara
# With ML plugin (GPU metrics + loss tracking)
pip install qara-ml
Configure
qara config init
This creates ~/.config/qara/config.toml. Open it and add your Telegram bot token and user ID:
[telegram]
bot_token = "123456:ABC-DEF..."
allowed_user_ids = [your_telegram_user_id]
How to get a bot token and user ID
- Message @BotFather on Telegram and send
/newbot - Follow the prompts to name your bot — you'll receive a token like
123456:ABC-DEF... - To find your user ID, message @userinfobot and it will reply with your numeric ID
- Important: Send any message to your new bot first so it can message you back
Start the daemon
qara daemon start
Run a process
qara run python train.py --name "experiment-1"
That's it. You'll receive Telegram notifications when the process starts, finishes, or crashes.
Usage
Process management
# Run a process
qara run python train.py --name "my-job"
# Attach to an existing process
qara attach 12345 --name "background-job"
# List watched processes
qara status
qara status --format json
# View completed runs
qara history --last 10
qara history --format json
Telegram commands
Once the daemon is running, send these commands to your bot:
| Command | Description |
|---|---|
/status |
List all watched processes |
/kill <name> |
Send SIGTERM to a process (escalates to SIGKILL) |
/history |
Show recent completed runs |
/logs <name> |
Get last N lines of stdout |
Daemon management
qara daemon start # Start in background
qara daemon start --foreground # Start in foreground (for systemd/launchd)
qara daemon stop # Stop the daemon
qara daemon status # Check if daemon is running
Install as a system service
# Auto-detects systemd (Linux) or launchd (macOS)
qara install
# Preview what would be installed
qara install --dry-run
# Remove the service
qara uninstall
Configuration
Full config.toml reference:
[daemon]
log_level = "INFO" # DEBUG, INFO, WARNING, ERROR
[telegram]
bot_token = "YOUR_BOT_TOKEN"
allowed_user_ids = [123456789]
[telegram.notifications]
on_start = true
on_finish = true
on_crash = true
stdout_tail_lines = 20 # lines of stdout included in finish notification
[commands]
enabled = ["status", "kill", "restart"]
kill_timeout_seconds = 10
[commands.allowed_scripts]
# alias = "/absolute/path/to/script.py"
[plugins]
enabled = ["ml"]
[plugins.ml]
gpu_poll_interval_seconds = 5
loss_pattern = "" # custom regex, leave empty for default
ML Plugin
The qara-ml plugin adds GPU monitoring and training loss tracking:
pip install qara-ml
Enable it in config.toml:
[plugins]
enabled = ["ml"]
After a training run finishes, you'll receive an additional summary:
GPU summary:
Peak VRAM: 18204 MB / 24576 MB
Avg GPU util: 94%
Peak temp: 78°C
Training summary:
Final loss: 0.0342
Best loss: 0.0298 (step 4200)
The loss tracker matches common patterns like loss=0.123, train_loss: 0.456, etc. You can provide a custom regex via loss_pattern in the config.
Architecture
qara daemon start
│
▼
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Watcher │────▶│ EventEngine │────▶│ Telegram │
│ (per proc) │ │ (pub/sub) │ │ Channel │
└─────────────┘ └──────┬───────┘ └──────────────┘
│
┌──────┴───────┐
│ Plugins │
│ (GPU, loss) │
└──────────────┘
▲
│ IPC (Unix socket)
│
qara run / qara status / ...
- Watcher spawns or attaches to processes via
asyncio.create_subprocess_exec - EventEngine is sequential async pub/sub — handlers receive events in subscription order
- Plugins subscribe directly to the engine and receive all events including stdout/stderr lines
- NotificationBus filters internal events before routing to channels
- IPC uses newline-delimited JSON over Unix sockets (Linux/macOS)
Development
git clone https://github.com/warptengood/qara
cd qara
uv sync --group dev
# Run checks
uv run ruff check src/ # lint
uv run ruff format src/ # format
uv run mypy src/ # type check
uv run pytest # tests
See CONTRIBUTING.md for guidelines.
License
MIT — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qara-0.1.1.tar.gz.
File metadata
- Download URL: qara-0.1.1.tar.gz
- Upload date:
- Size: 2.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c09c84f7780b221ffd08fae7535df1849a754d26115aadd43b25cf2df2f00dc
|
|
| MD5 |
a8d192bb840aa7914ec94b54c3154948
|
|
| BLAKE2b-256 |
c430d904245a72514ad5bbd1665c7dab5c4e688cf444ed51720b8f86240de790
|
Provenance
The following attestation bundles were made for qara-0.1.1.tar.gz:
Publisher:
publish.yml on warptengood/qara
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qara-0.1.1.tar.gz -
Subject digest:
7c09c84f7780b221ffd08fae7535df1849a754d26115aadd43b25cf2df2f00dc - Sigstore transparency entry: 1293552910
- Sigstore integration time:
-
Permalink:
warptengood/qara@1052d766e9db1f4161e0fcfd2b72df4342abdc48 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/warptengood
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1052d766e9db1f4161e0fcfd2b72df4342abdc48 -
Trigger Event:
push
-
Statement type:
File details
Details for the file qara-0.1.1-py3-none-any.whl.
File metadata
- Download URL: qara-0.1.1-py3-none-any.whl
- Upload date:
- Size: 29.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c4549fa98d6df56e52d6bf7535f5e59461dcc2b04a5d4a99be2ffbe353016de
|
|
| MD5 |
63e512c69122073da121646dd89dec27
|
|
| BLAKE2b-256 |
8d420e09efb97b31945b1bd4757d28e2fc9323c6b1f9563db27d3e22147cf71d
|
Provenance
The following attestation bundles were made for qara-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on warptengood/qara
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qara-0.1.1-py3-none-any.whl -
Subject digest:
1c4549fa98d6df56e52d6bf7535f5e59461dcc2b04a5d4a99be2ffbe353016de - Sigstore transparency entry: 1293552920
- Sigstore integration time:
-
Permalink:
warptengood/qara@1052d766e9db1f4161e0fcfd2b72df4342abdc48 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/warptengood
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1052d766e9db1f4161e0fcfd2b72df4342abdc48 -
Trigger Event:
push
-
Statement type: