first commit
This commit is contained in:
@@ -0,0 +1,421 @@
|
||||
# 🔥 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.
|
||||
Reference in New Issue
Block a user