DEV Community

Cover image for I got tired of watching my terminal, so I built GuGa
Positive Matician
Positive Matician

Posted on

I got tired of watching my terminal, so I built GuGa

I lost count of how many times I've started a long training run, gone to make coffee, come back ten minutes later, and found it had crashed thirty seconds after I left. Or worse — left it running overnight, woken up, and had no idea if it finished two hours ago or is still grinding away.

The usual suggestions are: set up a Discord webhook, use a Slack bot, write a wrapper script that sends an email. All of which require accounts, tokens, configuration, and maintenance. I just wanted my phone to buzz when my code was done.

So I spent last weekend building GuGa Nexus.

What it does

You prefix any terminal command with guga and when it finishes, a notification hits your phone or browser with the exit status, elapsed time, and the last line of output.

guga python train.py --epochs 100
Enter fullscreen mode Exit fullscreen mode

When training finishes, you get:

✅ python train.py done — 2h 14m
Epoch 100/100 — accuracy: 0.9431
Enter fullscreen mode Exit fullscreen mode

Or if it crashed:

❌ python train.py failed (exit 1) — 43s
CUDA out of memory. Tried to allocate 2.00 GiB
Enter fullscreen mode Exit fullscreen mode

That last line of stdout is the part I find most useful. You don't have to go back to the terminal to find out what happened — the relevant output is right in the notification.

It also works as a plain pipe for quick messages:

echo "Deploy done" | guga
guga "Checkpoint saved"
Enter fullscreen mode Exit fullscreen mode

How it works

GuGa runs a small Flask + Socket.IO server on your Linux machine as a systemd daemon. When you call guga, it POSTs to that local server, which relays the message over an encrypted WebSocket to whatever clients are connected.

For internet access it uses a Cloudflare Tunnel — so your phone can receive notifications even when you're not on the same network, with no domain purchase, no port forwarding, and no VPS. The tunnel URL is ephemeral and everything is AES-256-GCM encrypted end to end.

The server exposes a browser client too, so you don't need to install anything on your phone if you don't want to. Open the URL in any browser and notifications stream in real time. There's also an Android app for persistent background notifications.

Getting started

pip install guga
guga --install-service
Enter fullscreen mode Exit fullscreen mode

--install-service asks two questions — LAN or internet mode, and whether you want OS notifications forwarded — then sets up the systemd daemon, downloads cloudflared if needed, and prints the pairing QR code. That's the entire setup.

After that:

guga --qr         # get your connection URL / QR code
guga --show-pin   # get your pairing PIN for first connection
Enter fullscreen mode Exit fullscreen mode

Open the URL in a browser or scan it with the Android app, enter the PIN, and you're live.

The OS notification forwarding

One feature that surprised me by being more useful than I expected: if you enable it during setup, GuGa listens to your Linux desktop's D-Bus notification bus and forwards every system notification to your phone in real time. Calendar reminders, build completions, anything your OS surfaces. It runs alongside the server automatically.

I'm planning urgency-based filtering for this — critical alerts instant, low-priority ones bundled into a digest — but for now it forwards everything.

What's not perfect yet

I want to be upfront about one limitation: guga --show-pin retrieves the latest pending pairing PIN from the server. This assumes only one device is trying to pair at a time. If multiple devices hit the pairing endpoint simultaneously, multiple PINs get generated and --show-pin would only show the latest one.

The fix I'm working on inverts the model — the connecting device generates and displays its own PIN, sends it to the server, and the user confirms the one they see on their own screen. This way the server never generates PINs and concurrent attempts can't interfere with each other. Until that lands, pairing over LAN first is the safe approach.

Links

If you try it I'd genuinely like to know where the setup flow breaks or what's confusing — this is still early and real feedback is more useful than stars right now.

Top comments (0)