422 lines
12 KiB
Markdown
422 lines
12 KiB
Markdown
# 🔥 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
|
||
|
||
```bash
|
||
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
|
||
|
||
1. Start Flare
|
||
2. Open the dashboard URL from the startup banner
|
||
3. Source one wrapper into your script
|
||
4. Call helper functions like `mon`, `mon_detail`, `mon_complete`, or `mon_error`
|
||
5. Watch events arrive live in the browser
|
||
6. Export JSON or CSV if you want to keep the record
|
||
|
||
All runtime data lives in memory only.
|
||
|
||
---
|
||
|
||
## Project Layout
|
||
|
||
```text
|
||
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.
|
||
|
||
```bash
|
||
mon "message"
|
||
mon_complete "message"
|
||
mon_error "message"
|
||
mon_detail "message" '{"key":"value"}'
|
||
```
|
||
|
||
- `mon` — normal running/progress update
|
||
- `mon_complete` — successful completion
|
||
- `mon_error` — failure / error state
|
||
- `mon_detail` — progress update with structured JSON detail
|
||
|
||
Example:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
#!/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
|
||
|
||
```yaml
|
||
#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
|
||
|
||
```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:
|
||
|
||
```text
|
||
/<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:
|
||
|
||
```text
|
||
/<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-summary` metadata 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
|
||
|
||
```text
|
||
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
|
||
|
||
- `--topic` still exists internally, but the dashboard is host-oriented in practice.
|
||
- `--get-path` lets you pin the browser-facing path if you do not want a generated one.
|
||
- `--post-path` lets you pin the ingest path for controlled testing.
|
||
- `--verbose` is 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 validation
|
||
- `src_test.py` — source the real wrappers, then exercise `mon`, `mon_detail`, `mon_complete`, and `mon_error`
|
||
|
||
Both use one environment variable:
|
||
|
||
```bash
|
||
export __FLARE_SRC='http://SERVER:8080/PRINTED_GET_PATH/wrapper.sh'
|
||
```
|
||
|
||
Then run:
|
||
|
||
```bash
|
||
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
|
||
|
||
```text
|
||
┌──────────────┐ 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.
|