docs(readme): restore the star history chart with a sealed token (#3844)

The chart in the README has been rendering "GitHub restricted access to
star data" rather than a chart. GitHub [limited stargazer
data](https://github.blog/changelog/2026-06-30-upcoming-access-restrictions-to-public-api-endpoints-and-ui-views/)
to a repository's admins and collaborators, and a hosted chart service
calls that endpoint as itself — it is nobody's collaborator, so it
renders a placeholder instead. star-history accepts a `sealed_token`,
one of our GitHub tokens encrypted with their public key, which restores
the real chart.

Verified against the exact URL this adds: HTTP 200, no `x-chart-status:
restricted`, and a 64KB SVG whose axis labels are real data (2026 /
April / July, 1K through 6K) rather than the placeholder text.

The token behind it is fine-grained and scoped to this repository alone,
with Contents read and write. Write access is what GitHub accepts as
proof of collaborator status — that requirement is the whole reason the
chart broke, and it is genuinely required rather than star-history being
confused: the same Actions token gets 403 from both REST and GraphQL
under `contents: read` and reads the full history under `contents:
write`.

It expires **16 Aug 2027**, and when it lapses the chart reverts to the
placeholder with no other signal — no failing check, no notification.

The sealed value is a ciphertext only star-history can decrypt, so
publishing it is safe on its own — a copied `sealed_token` replayed
against another repository returns the same restricted placeholder,
since GitHub gates on the token's own access rather than on the sealing.
What the fine-grained scope buys is separate: a bound on star-history,
who decrypt the token to call GitHub and so hold a credential that can
push here until it expires.

<details>
<summary>Alternatives considered</summary>

Earlier commits on this branch built and then removed two self-hosted
approaches, both of which avoided giving star-history a token at all:

- **Render our own chart** — a script paging the stargazers API over
GraphQL and emitting an SVG, published to an orphan branch and later
generated into the docs site. Worked end to end, but cost 237 lines to
maintain and produced a chart that isn't the one people recognise.
- **Render star-history's chart ourselves** — their MIT renderer driven
by our own data, fully offline. Produces the identical chart, but needs
Node and ~105 npm packages in CI plus a pinned clone of their app
internals.

Both are in this branch's history if the sealed-token arrangement ever
stops appealing. The deciding argument against them: GitHub's camo proxy
already stands between README readers and star-history's servers, and
the sealed token is a ciphertext rather than a credential in the clear,
so the privacy case for self-hosting was weaker than it first looked —
while the maintenance cost was real.
</details>

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maximilian Roos
2026-08-17 23:07:22 -07:00
committed by GitHub
parent 8faeabf239
commit 2aab2ba1a2
2 changed files with 32 additions and 2 deletions
+16 -1
View File
@@ -245,6 +245,21 @@ The `-x` flag runs a command after switching; arguments after `--` are passed to
### Star history
<!-- `sealed_token` is a GitHub token of ours encrypted with star-history's public
key, from "Generate embed code" on star-history.com. Without one the chart
renders a "GitHub restricted access to star data" placeholder: GitHub limits
stargazer data to a repo's admins and collaborators, and their servers are
neither.
The token is fine-grained and reaches only this repo. Its Contents permission
has to be read *and write* — write access is what GitHub accepts as proof of
collaborator status, so rotating to a read-only token brings the placeholder
straight back. Publishing the ciphertext is safe on its own; what the scope
buys is a bound on star-history, which decrypts it and so holds a credential
that can push here until it expires 2027-08-16 — at which point the chart
reverts to the placeholder with nothing else to signal it. star-history
reports GitHub is working on restoring access, so drop this parameter once
the plain URL renders a chart again. -->
<a href="https://star-history.com/#max-sixty/worktrunk&Date">
<img src="https://api.star-history.com/svg?repos=max-sixty/worktrunk&type=Date" width="500" alt="Star History Chart">
<img src="https://api.star-history.com/svg?repos=max-sixty/worktrunk&type=Date&sealed_token=2ySbQiVbkVrGmwgDsJya-xr4ApbVVvR0siYI46d22Xj_1kPCcgA9X0YpUGc3__aMuZ0ZAWzG4NBhJtqepYjlkoYrVwmKbgaPmGpNZTCfSyVp8EDA_IXaOOYW2whsOXDAi6g7HD9ezsnqSl58n7AqW2_4IQ4hY2p3h7tNv_3k4am5ASq1NdpbfrOUazCl" width="500" alt="Star History Chart">
</a>
@@ -245,6 +245,21 @@ The `-x` flag runs a command after switching; arguments after `--` are passed to
### Star history
<!-- `sealed_token` is a GitHub token of ours encrypted with star-history's public
key, from "Generate embed code" on star-history.com. Without one the chart
renders a "GitHub restricted access to star data" placeholder: GitHub limits
stargazer data to a repo's admins and collaborators, and their servers are
neither.
The token is fine-grained and reaches only this repo. Its Contents permission
has to be read *and write* — write access is what GitHub accepts as proof of
collaborator status, so rotating to a read-only token brings the placeholder
straight back. Publishing the ciphertext is safe on its own; what the scope
buys is a bound on star-history, which decrypts it and so holds a credential
that can push here until it expires 2027-08-16 — at which point the chart
reverts to the placeholder with nothing else to signal it. star-history
reports GitHub is working on restoring access, so drop this parameter once
the plain URL renders a chart again. -->
<a href="https://star-history.com/#max-sixty/worktrunk&Date">
<img src="https://api.star-history.com/svg?repos=max-sixty/worktrunk&type=Date" width="500" alt="Star History Chart">
<img src="https://api.star-history.com/svg?repos=max-sixty/worktrunk&type=Date&sealed_token=2ySbQiVbkVrGmwgDsJya-xr4ApbVVvR0siYI46d22Xj_1kPCcgA9X0YpUGc3__aMuZ0ZAWzG4NBhJtqepYjlkoYrVwmKbgaPmGpNZTCfSyVp8EDA_IXaOOYW2whsOXDAi6g7HD9ezsnqSl58n7AqW2_4IQ4hY2p3h7tNv_3k4am5ASq1NdpbfrOUazCl" width="500" alt="Star History Chart">
</a>