Releases: 0xzerolight/watchpost
Release list
v1.3.0
Added
- Start page (Settings -> Start page). Which of the three nav pages watchpost opens on.
Changed
-
The repositories dashboard has its own path,
/repos. -
No movement badge on the "All" period (repo page KPI tiles, Analytics totals).
v1.2.0
Added
-
An Analytics page (third nav entry). Portfolio totals over a combined star curve, a ranked
repo table and a recent-changes feed. -
A sync interval (Settings -> Sync every). Set
10m,6hor1h 30minstead of a cron
expression, applied without a restart. -
CSV and JSON export per repo (repo page header).
Changed
-
The repo page leads with KPI tiles over one hero chart. Clicking a tile switches the metric
the chart plots. -
Views and clones plot as bars.
-
Recessive chart gridlines, an HTML tooltip and event markers that rest as dots.
-
The recent-changes feed moves from the dashboard to the Analytics page.
v1.1.0
Added
- GHCR container pull counts, auto-detected. Every sync also fetches each tracked repo's
public package page (github.com/{owner}/{repo}/pkgs/container/{name}) and charts the
cumulative pull count as a "Container pulls" card on the repo page. Scraped, because no GitHub
API exposes the number; unauthenticated, so it costs no token scope and no rate budget. A repo
without a package named after it 404s and is skipped — zero configuration. A failed scrape is a
partial sync like any failing endpoint, and deliberately does not count toward the total-failure
verdict that backs a repo off. Schema migration v4 adds thecontainer_pullstable (day-keyed,
monotonic MAX, same rules as release assets).
Changed
-
The repo charts are redrawn on a validated palette. Each series is a gradient-filled line on
a palette checked for colourblind separation and 3:1 contrast against the card surface. The axis
borders and vertical gridlines are gone, dates readAug 19rather than2026-08-19, counts past
five digits read12.3K, and the tooltip takes the card's own surface and ink instead of the
library default. Event markers drop to a half-strength dashed line under a ringed dot, so a
marker sits behind the data it annotates rather than across it. -
Event-kind chips carry their colour on a dot, not in the text. The label is body ink at every
size, which frees the palette from having to be readable as small text and lets its two lightest
slots be used as marks. -
Chart cards with no observed data are hidden. A repo that ships only docker images no longer
shows a blank Downloads pane, and repos without container packages don't get a blank pulls pane;
the same rule covers views/clones cards where the token lacks traffic permissions. A repo with
nothing observed at all keeps its existing empty state.
v1.0.0
Added
- One-line install.
curl … | bashon Linux and macOS,irm … | iexon Windows. Pulls the published image, writesPUID/PGIDso the bind-mounteddata/is writable whatever the host uid is, waits for/health, and opens the setup page.scripts/update.shupdates in place. - First-run setup page. An install with no
WATCHPOST_GITHUB_TOKENboots and redirects every page to/setup, where a pasted token is checked against GitHub before it is saved and collection starts immediately. Replaceable from settings afterwards; only the last four characters are ever rendered. - Multi-arch images at
ghcr.io/0xzerolight/watchpost,linux/amd64andlinux/arm64, built on a tag push. PUID/PGIDsupport. The container entrypoint aligns the data directory with the host user and drops privileges, replacing the manualchown -R <uid> data.WATCHPOST_TZ. Displayed times - last synced, the--doctorrate-limit reset, the day a new event defaults to - render in the configured IANA zone instead of always UTC. Stored dates, chart day buckets andWATCHPOST_CRONstay UTC.- Real error and loading states. A failed request says so instead of failing silently, and every request that can be waited on has a loading indicator and a disabled state.
- Accessible navigation. Skip link, current-page state in the nav, focus that survives a fragment swap, visible sort-direction indicators, and delete confirmation in a real dialog.
- Faster page loads. gzip compression, plus content-hashed asset URLs served
immutablefor a year and revalidated with a 304. - Safe migrations. The database is copied to
data/watchpost.v{schema}.{timestamp}.bakbefore a schema upgrade and the newest three are kept. Schema v2 adds the indexes the page queries actually use. - AGPL-3.0-or-later license.
Changed
WATCHPOST_GITHUB_TOKENis optional. Missing is no longer a startup error; it is the state the setup page exists to resolve. Schema v3 adds asettingstable to hold a token saved there.--doctorreports token provenance. Which token is in use and where it came from, and an install with none fails with a pointer to the setup page rather than a request that was never made.- Rebuilt pages. One shared component set - real labels on every form field, consistent headings, empty states, and a restructured repo, settings and index page. Spacing, type and colour are design tokens from a single source, and the event-kind chips meet WCAG AA contrast.
- Cheaper charts and queries. A period change re-scales the existing charts instead of rebuilding them; the repo overview is one query rather than one per repo, with the delta recompute bounded to the window that can have changed.
- Inline event handlers are gone, replaced by delegated listeners.
- Minimum supported Rust version is 1.88. Docker base images are pinned to explicit versions and the build caches its dependency layer; CI runs a locked build, an MSRV check, a Docker build and an advisory audit.
Fixed
- The Add event form pre-filled the UTC day, so between local midnight and the UTC rollover it defaulted to the wrong date.
- A line chart with only one or two observed days drew nothing at all - the Downloads card on a repo whose releases were first read this week was an empty plot area. Such points now get a marker.
- Rate-limit classification is limited to 403 and 429; a transient 5xx is retried instead of being counted as a rate limit.
- Daily stats record the last observation of the day rather than the intraday maximum.
- Repo writes are transactional, and a partial sync no longer inflates the error streak or the backoff behind it.
- A database written by a newer build is refused at open with the version pair and the fix, instead of being served against a schema the binary does not know.
- Backup pruning keys off the embedded timestamp, so a v10 database no longer discards its newest backups first.
- The health endpoint verifies the database and queries the live schema.
- Background polls no longer clear the error toast or clobber a pending focus target.
- Enter submits the picker and edit-row forms, and sort links carry the client-side zoom instead of resetting it.
- Chart tooltips are clamped to the canvas, reduced-motion preferences are honoured, and the axis labels are readable.
- A poisoned mutex is recovered and a panicking handler is caught, rather than taking down the worker behind it.
- Configuration is validated at startup: the token, the API base URL and the log filter.
Security
- Security headers on every response. Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, Referrer-Policy and Cross-Origin-Opener-Policy. The policy allows no inline script or style at all (
script-src 'self',style-src 'self'), so an injected<script>oronerror=does not execute even if it survives the escaping. - CSRF hardening. The cookie is checked for the shape this server mints, and carries
Securewhen the request arrived over HTTPS plus a 30-dayMax-Age. Repo discovery is a CSRF-gated POST; it used to be a GET that spent API calls as a side effect. - Error responses carry no internal detail - no paths, no SQL, no upstream error strings.
- There is no authentication. Anyone who can reach the port has full read and write.
compose.ymlpublishes to127.0.0.1:8080rather than every interface, so the default deployment is not reachable from the network - bind it to the loopback or put it behind a proxy that authenticates.