🔥 Flare
Ephemeral, zero-dependency script telemetry for real-world automation.
Run one Python file, open the dashboard URL it prints, source one wrapper line into a script, and watch that script report back in real time.
Flare is built for short-lived operational work:
- deployments
- provisioning
- migrations
- maintenance windows
- demos and labs
- one-off admin scripts
It is intentionally not a long-term logging stack. There is no database, no accounts, no agents, and no setup beyond python3 flare.py.
What Flare Is
Flare gives scripts and small automation jobs a disposable telemetry dashboard.
Instead of hoping a job is still alive, your script can emit structured updates like:
- starting
- installing packages
- waiting for service
- completed
- failed
Those updates appear live in the browser, grouped by host.
Flare is a disposable telemetry dashboard.
Why It Exists
Most observability tooling is too heavy for quick operational work.
If all you want is a live view into a script, standing up a whole logging or monitoring platform is overkill.
Flare id the opposite:
- fast to start
- easy to discard
Quick Start
python3 flare.py
That is all you need.
On startup, Flare prints:
- a ready-to-open dashboard URL
- wrapper import lines for bash / bash+python / PowerShell
- exportable environment variables if you want to post manually
- the exact per-run GET and POST capability URLs baked into this launch
Open the dashboard URL in a browser, then paste one of the printed wrapper lines into a script.
Basic Workflow
- Start Flare
- Open the dashboard URL from the startup banner
- Source one wrapper into your script
- Call helper functions like
mon,mon_detail,mon_complete, ormon_error - Watch events arrive live in the browser
- Export JSON or CSV if you want to keep the record
All runtime data lives in memory only.
Project Layout
flare/
├── flare.py # server, wrappers, in-memory pub/sub
└── flare.html # browser dashboard
flare.py uses only the Python standard library.
Wrapper Options
Pick whichever wrapper fits the target environment.
| Wrapper | Command shape | Requirements |
|---|---|---|
wrapper.sh |
eval "$( curl -s <printed-wrapper-url> )" |
bash, python3 |
wrapper.bash |
eval "$( curl -s <printed-wrapper-url> )" |
bash 4+, curl, openssl |
wrapper.ps1 |
iex (irm <printed-wrapper-url>) |
PowerShell 5.1+ or pwsh 7+ |
All wrappers bake in the current run’s:
- event destination URL
- session/topic identifier
- bearer token
- HMAC secret, unless disabled
Your script only needs to call helper functions.
mon "message"
mon_complete "message"
mon_error "message"
mon_detail "message" '{"key":"value"}'
mon— normal running/progress updatemon_complete— successful completionmon_error— failure / error statemon_detail— progress update with structured JSON detail
Example:
mon "starting backup"
mon_detail "installing packages" '{"packages":["nginx","fail2ban"]}'
mon_complete "backup finished"
Summary Channel
There is one reserved mon_detail title with special dashboard behavior:
mon_detail "flare-summary" '{"site":"phx-1","phase":"patching","owner":"shane"}'
Events sent with title flare-summary are treated as summary metadata for that host. They populate the main detail area instead of appearing as a normal log entry.
That is useful for:
- current phase
- change ticket / runbook identifiers
- target version
- site / cluster / environment
- host-specific notes you want pinned in view
Example: Bash Script
#!/usr/bin/env bash
set -euo pipefail
# Use the exact printed wrapper URL from flare.py
eval "$( curl -sf http://SERVER:8080/REPLACE_WITH_PRINTED_GET_PATH/wrapper.bash )" || {
echo "flare unavailable, stubbing functions" >&2
mon() { echo "[flare] $1" >&2; }
mon_complete() { mon "$1"; }
mon_error() { mon "$1"; }
mon_detail() { mon "$1"; }
}
mon_detail "flare-summary" '{"role":"db","change":"CHG-1234"}'
mon "starting server hardening"
mon_detail "system update" '{"action":"apt-get upgrade"}'
apt-get update -qq
apt-get upgrade -y -qq
mon "configuring firewall"
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw --force enable
mon_complete "hardening finished"
Example: Cloud-Init / First Boot
#cloud-config
runcmd:
- |
eval "$( curl -sf http://SERVER:8080/REPLACE_WITH_PRINTED_GET_PATH/wrapper.bash )"
mon_detail "flare-summary" '{"phase":"first boot","source":"cloud-init"}'
mon "cloud-init starting on $(hostname)"
apt-get update -qq && apt-get upgrade -y -qq
mon "packages updated"
mon_complete "$(hostname) ready"
Use the exact printed wrapper URL in real usage.
Example: PowerShell
# Use the exact wrapper URL printed by flare.py
iex (irm http://SERVER:8080/REPLACE_WITH_PRINTED_GET_PATH/wrapper.ps1)
mon_detail "flare-summary" '{"phase":"patching","window":"nightly"}'
mon "starting updates"
mon_detail "patching" '{"phase":"download"}'
mon_complete "updates complete"
Security Model
Flare is still lightweight, but it is more deliberate than a naive callback endpoint.
1. Per-run session values
Each launch generates fresh values unless you provide your own:
- bearer token
- HMAC secret
- topic/session identifier
- randomized browser-facing GET base path
- randomized POST ingest path
2. Randomized GET base path
Browser-facing resources live under a per-run random path, for example:
/<GET_PATH>/
/<GET_PATH>/flare.html
/<GET_PATH>/wrapper.sh
/<GET_PATH>/wrapper.bash
/<GET_PATH>/wrapper.ps1
/<GET_PATH>/events?topic=...
That makes sessions harder to casually enumerate and keeps one run’s browser-facing resources scoped to that run.
3. Randomized POST path
Event ingest lives at a separate per-run path, for example:
/<POST_PATH>
That separates “can view” from “can submit.” Knowing the dashboard URL does not automatically reveal the POST target.
4. Bearer auth
Event streaming and publish operations are tied to a token.
5. HMAC-signed event envelopes
Wrappers sign their event content before sending it. The server validates those signatures before accepting events, and the dashboard can also surface verification status for received events.
That means Flare is not just trusting any JSON that happens to hit the endpoint with the right shape.
Why the Paths Exist
Flare uses capability-style per-run URLs.
In plain English:
- the dashboard should not live at a predictable public root
- wrappers should be scoped to the current run
- event streaming should belong to the same run-specific namespace
- message ingest should be isolated from browser-facing resources
That is why the startup banner prints the exact dashboard URL, exact wrapper URLs, and exact POST target for that run.
You are not meant to memorize or handcraft these paths. Just copy what Flare prints.
Dashboard Features
The dashboard is intentionally lightweight and host-oriented.
Current behavior includes:
- real-time SSE event streaming
- host-tab grouping of activity
- running / complete / error visibility
- structured detail rendering
- reserved
flare-summarymetadata area - JSON export
- CSV export
- direct open from the startup banner URL
- client-side session behavior with no database backend
The dashboard URL is preconfigured with the session values it needs for that one run.
CLI Options
python3 flare.py [OPTIONS]
--host HOST Bind address (default: 0.0.0.0)
--port PORT Port (default: 8080)
--token TOKEN Bearer token (generated if omitted)
--secret SECRET HMAC secret (generated if omitted)
--topic TOPIC Session/topic name (generated if omitted)
--html PATH Path to flare.html (auto-detected if omitted)
--get-path PATH Browser-facing GET base path (generated if omitted)
--post-path PATH POST ingest path (generated if omitted)
--no-secret Disable HMAC signing
--verbose Debug / validation logging
Notes
--topicstill exists internally, but the dashboard is host-oriented in practice.--get-pathlets you pin the browser-facing path if you do not want a generated one.--post-pathlets you pin the ingest path for controlled testing.--verboseis useful when testing signatures and request behavior.
Endpoints
Flare’s stable shape is:
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /<GET_PATH>/ |
No | Dashboard |
| GET | /<GET_PATH>/flare.html |
No | Dashboard file |
| GET | /<GET_PATH>/wrapper.sh |
No | Bash + Python wrapper |
| GET | /<GET_PATH>/wrapper.bash |
No | Pure Bash wrapper |
| GET | /<GET_PATH>/wrapper.ps1 |
No | PowerShell wrapper |
| GET | /<GET_PATH>/events?topic=...&auth=...&since=all |
Token | SSE event stream |
| POST | /<POST_PATH> |
Token | Publish signed or unsigned event envelope |
In normal usage, you should not need to hit these by hand. The startup banner and wrappers handle it for you.
Tests
Two test helpers are included in this repo layout:
http_test.py— direct HTTP tests for weird paths, params, headers, methods, auth, SSE, and POST validationsrc_test.py— source the real wrappers, then exercisemon,mon_detail,mon_complete, andmon_error
Both use one environment variable:
export __FLARE_SRC='http://SERVER:8080/PRINTED_GET_PATH/wrapper.sh'
Then run:
python3 http_test.py
bash src_test.py
http_test.py derives the dashboard, wrapper, SSE, and POST targets from the wrapper exports. src_test.py exercises both wrapper.sh and wrapper.bash from that same seed URL.
Architecture
┌──────────────┐ source wrapper ┌─────────────┐
│ Remote Host │ ◄──────────────────────── │ flare.py │
│ or Script │ │ server │
│ │ │ │
│ mon() │ ───── signed POST ──────► │ in-memory │
│ mon_detail() │ │ storage │
│ mon_error() │ │ + SSE │
│ mon_complete() │ │
└──────────────┘ └──────┬──────┘
│
│ SSE
▼
┌─────────────┐
│ flare.html │
│ dashboard │
└─────────────┘
Everything is intentionally ephemeral. If you need to keep the output, export JSON or CSV from the dashboard before shutting it down.
Tips
Graceful fallback
If Flare is unavailable, stub the functions and let the script continue.
Use the printed URLs
Because the GET and POST paths are generated per run, copying the banner output is the safest workflow.
Export before exit
Flare does not persist anything. If the session matters, export it before shutting the server down.
License
Do whatever you want with it.