Skip to content

Fix broken main: define Config.public_only, and add a reviewed path to public serving - #1

Merged
tschm merged 2 commits into
mainfrom
fix/public-only-config
Aug 29, 2026
Merged

Fix broken main: define Config.public_only, and add a reviewed path to public serving#1
tschm merged 2 commits into
mainfrom
fix/public-only-config

Conversation

@tschm

@tschm tschm commented Aug 29, 2026

Copy link
Copy Markdown
Member

main is currently broken

github.py filters the repo listing on cfg.public_only, but the field was never added to Config. Every GitHub refresh raises:

File "/app/jq_collector/github.py", line 306, in <genexpr>
    or (cfg.public_only and ...)
AttributeError: 'Config' object has no attribute 'public_only'

This fails quietly. The local refresh loop is unaffected, so the collector keeps serving /metrics and the Prometheus target stays up — it just loses every CI, drift, pull-request and issue series, and the board shows No data on roughly two thirds of its panels. Confirmed by checking out main into a temp dir and instantiating Config.

First commit is the four-line fix, isolated so it can be reviewed or reverted on its own.

Second commit: serving the board world-readable

Restricting collection to public repos is not sufficient on its own, which is the part worth reviewing.

With anonymous access enabled, a visitor can POST arbitrary PromQL to /api/ds/query and read the raw label index via /api/datasources/proxy. Purged names linger in that index until head compaction — measured here, after deleting all seven private repos the index still returned every one of their names, and a Prometheus restart did not clear them. Deleting the data does not remove the names.

So this adds both halves:

  • docker-compose.public.yml — anonymous off, Grafana public dashboards on. A public link serves only that dashboard's own queries with no datasource behind it. Verified: public link and its panel queries 200; /api/search, /api/ds/query and the proxy all 401.
  • scripts/check-public-safe.sh — the preflight. Verifies each exported repo is public on GitHub rather than trusting our own visibility label, that anonymous queries and the proxy are refused, that Prometheus and the collector stay loopback-only, and that no public link is live while JQ_PUBLIC_ONLY is off. Exits non-zero otherwise.
  • scripts/make-public-dashboard.py — public dashboards don't resolve template variables, so every panel filtered on $repo renders No data behind a public link. This generates fleet-public.json from fleet.json with the variable and its selectors stripped.
  • check-dashboard.py now validates every dashboard in the folder, so the generated copy can't drift unnoticed.

Verification

No CI exists in this repo, so this was checked by hand from a clean git archive of the branch:

Check Result
Fix commit alone resolves the crash Config.public_only = True
Full branch, real collection against GitHub 23 repos, 1018 samples, no private repo leaked
fleet-public.json regenerates from source byte-identical
Preflight refuses an unsafe config fails on anonymous mode, passes on the overlay

Default behaviour is unchanged: JQ_PUBLIC_ONLY defaults to false and the overlay is opt-in.

🤖 Generated with Claude Code

tschm and others added 2 commits August 29, 2026 12:09
github.py filters the repo listing on `cfg.public_only`, but the field was
never added to Config, so `collect()` raised AttributeError on every pass:

    File "/app/jq_collector/github.py", line 306, in <genexpr>
      or (cfg.public_only and ...)
    AttributeError: 'Config' object has no attribute 'public_only'

The local refresh loop is unaffected, so the collector keeps serving and the
target stays "up" - it just silently loses every CI, drift, pull-request and
issue series, and the board shows No data for two thirds of its panels.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Filtering private repos out of collection is not sufficient on its own. With
anonymous access enabled, a visitor can POST arbitrary PromQL to /api/ds/query
and read the raw label index through /api/datasources/proxy - and purged names
linger in that index until head compaction, so deleting the data does not
remove the names. Measured on this stack: after purging all seven private
repos, the index still returned every one of them, and a Prometheus restart
did not clear it.

So this adds both halves:

- docker-compose.public.yml turns anonymous access off and Grafana's
  public-dashboard feature on. A public link serves only that dashboard's own
  queries with no datasource behind it; /api/search, /api/ds/query and the
  proxy all return 401.
- scripts/check-public-safe.sh is the preflight. It verifies each exported
  repo is public *on GitHub* rather than trusting our own label, that
  anonymous queries and the proxy are refused, that Prometheus and the
  collector stay loopback-only, and that no public link is live while
  JQ_PUBLIC_ONLY is off. Non-zero exit if not.
- Public dashboards do not resolve template variables, so every panel filtered
  on $repo renders No data behind a public link. make-public-dashboard.py
  generates fleet-public.json from fleet.json with the variable and its
  selectors stripped.
- check-dashboard.py now validates every dashboard in the folder, not just
  fleet.json, so the generated copy cannot drift unnoticed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tschm
tschm merged commit 95fce1c into main Aug 29, 2026
1 check passed
@tschm
tschm deleted the fix/public-only-config branch August 29, 2026 08:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant