If you discover a security vulnerability in Linknode Energy Monitor, please report it privately — do not open a public GitHub issue.
- Email: murr2k@gmail.com
- Include: a description of the issue, affected URL/endpoint or component, and steps to reproduce (a proof-of-concept if available).
- You can expect an initial acknowledgement within a few days. Confirmed issues will be fixed and deployed as a priority; please allow reasonable time for a fix before any public disclosure.
Responsible disclosure is appreciated and credited — for example, the Grafana
anonymous-access hardening in 1.2.0 was the result of an external report (see
CHANGELOG.md).
Production is reachable at:
https://linknode.com(andwww): static site served by a Cloudflare Worker (linknode-web, configweb/wrangler.jsonc)https://linknode-eagle-monitor.fly.dev: power-monitoring ingest and API, the only Fly.io app (Flask, SQLite on theeagle_datavolume)https://energy.linknode.com: a Cloudflare redirect rule (301 tohttps://linknode.com/#energy-dashboard); no service behind it
Out of scope (decommissioned; historical docs only, under docs/archive/):
- The Fly apps
linknode-web(nginx),linknode-grafana(Grafana) andlinknode-influxdb(InfluxDB), destroyed on 2026-09-27 - The retired Rackspace/Kubernetes deployment
- No secrets are stored in this repository. Credentials live in Fly.io
secrets and GitHub Actions secrets only:
- Fly (
linknode-eagle-monitor):EAGLE_PASSWORD(ingest Basic auth),SLACK_WEBHOOK_URL,PUSHOVER_API_TOKEN,PUSHOVER_USER_KEY - GitHub:
FLY_API_TOKEN(Fly deploy),CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_ID(site deploy) - The Raspberry Pi uploader keeps its credentials in
/etc/eagle-bypass.env(root, mode 0600) on the Pi, never in the repo
- Fly (
- Retired:
INFLUXDB_TOKENandGRAFANA_ADMIN_PASSWORD(GitHub), and the per-app secrets of the destroyed Fly apps. .envand*.secret.*files are git-ignored and must never be committed.- Credentials are rotated when exposure is suspected.
- Grafana anonymous Admin (fixed January 2026).
GF_AUTH_ANONYMOUS_ORG_ROLEwasAdmin, giving any anonymous visitor full admin rights (edit/delete dashboards and datasources). Externally reported and changed toViewer. Grafana was retired on 2026-09-27. - Committed InfluxDB token (rotated January 2026). The token
my-super-secret-auth-tokenwas committed and lived in git history; it was rotated and revoked. InfluxDB was retired on 2026-09-27.
- Transport: TLS/HTTPS enforced at Cloudflare (site) and the Fly proxy (API).
- Site headers: Content-Security-Policy, HSTS, X-Frame-Options,
X-Content-Type-Options, Referrer-Policy and Permissions-Policy set in
web/public/_headers.connect-srcallows only the site itself andhttps://linknode-eagle-monitor.fly.dev;frame-srcis'none'. Rocket Loader is off for the zone because it conflicts with the CSP. - Ingest:
POST /eaglerequires HTTP Basic auth and is rate limited (60 requests/minute per client). - Read API:
/api/stats,/api/dashboard,/api/streamand/healthare public and read-only (the optionalEAGLE_API_KEYis not set). CORS is limited to an allow-list of site origins; the SSE stream sendsAccess-Control-Allow-Origin: *. - Data: the
eagle_datavolume is encrypted at rest, with daily snapshots kept 14 days. - CI/CD: automated security scanning runs on pushes and pull requests
(
.github/workflows/security-scan.yml), including a check that the required headers are present inweb/public/_headers.
If you fork and self-host:
- Set every secret via your platform's secret store (Fly secrets, GitHub secrets, or equivalent) — never inline in config or scripts.
- Generate strong, unique tokens/passwords; rotate them on a schedule.
- Keep the ingest endpoint behind authentication; set an API key if the read endpoints should not be public.
- Terminate TLS at the edge and keep HSTS + CSP enabled.
- Apply authentication and rate limiting to any publicly exposed write endpoint.