fix(privacy): fetch the star count server-side, drop all third-party calls - #8
Merged
Conversation
β¦calls The footer fetched api.github.com from the visitor's browser on every page load, paste pages included. On a zero-knowledge paste service that is the wrong shape: opening a secret link disclosed the reader's IP, User-Agent and the timing of the read to a third party. The service promises the server cannot see your content, then had the client announce itself elsewhere. The count is now fetched by the server at most once an hour and rendered into the page. GITHUB_REPO="" disables the outbound call entirely for air-gapped deployments; the footer then links to GitHub without a number, and never shows a misleading 0 when the count is simply unknown. Refreshing runs as a background task started in lifespan and cancelled on shutdown, with the fetch on a worker thread so a hanging GitHub cannot block the event loop. Failures keep the previous value and are logged, never raised: a vanity counter must not affect serving pastes. Uses urllib rather than httpx β httpx is a test-only dependency here, and webhook.py already establishes stdlib HTTP as the convention for outbound calls. With nothing calling out any more, connect-src drops back to 'self'. Verified in a real browser on both page types: zero third-party requests, no CSP violations, footer renders the count, and a paste round-trips end to end.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contexte
static/footer.jsfetchedapi.github.comfrom the visitor's browser on every page load β including paste pages.On a zero-knowledge paste service that is the wrong shape. Opening a secret link disclosed the reader's IP, User-Agent and the timing of the read to a third party. The service promises the server cannot read your content, and then had the client announce itself somewhere else. It also cost a third-party connection on the critical path of every page load.
Changements
app/stars.pyβ the count is fetched server-side, at most once an hour, and rendered into the page. Background task started inlifespanand cancelled on shutdown; the fetch runs on a worker thread so a hanging GitHub cannot block the event loop. Failures keep the previous value and are logged, never raised β a vanity counter must not affect serving pastes.GITHUB_REPOsetting (defaultstackopshq/ghostbit, documented in.env.example). Set it to""and the outbound call disappears entirely, for air-gapped deployments.0β which is what the old markup shipped in the HTML before the fetch resolved.static/footer.jsdeleted, andconnect-srcdrops back to'self'now that nothing calls out.urllibrather thanhttpx: httpx is a test-only dependency here, andwebhook.pyalready establishes stdlib HTTP as the convention for outbound calls. No new runtime dependency.Tests
Browser-verified against the running app, on both page types:
The paste page was reached by actually creating a paste through the UI, so the end-to-end encrypt-and-submit flow is exercised too. Separately, booting with
GITHUB_REPO=""rendersβ Star us on GitHubwith no errors.118 tests pass β three added: no
api.github.comin either page's HTML,connect-srcstays'self'-only, and the footer degrades correctly with and without a count.ruff check/ruff format --checkclean.Risques
The server now makes an outbound call it did not make before, which is a change in deployment posture: air-gapped or egress-filtered installs will log a warning hourly. That is why
GITHUB_REPO=""exists and why every failure path keeps serving pages. The alternative β deleting the counter outright β was rejected as a product decision that is not mine to make; this preserves the feature while removing the leak.One behaviour change worth noting: the count is now identical for all visitors and up to an hour stale, rather than per-browser cached in
localStorage. For a star count that is fine, and it removes twolocalStoragekeys from visitors' browsers.