Changelog
All notable changes to this project are documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
Added
- Collaborative 64×64 pixel canvas (Rust / axum) with an embedded web UI.
- Real-time updates over Server-Sent Events, shared across instances with Redis
pub/sub; in-memory fallback when no
REDIS_URLis set. - HTTP API:
/health,/info(with serving instance),/api/canvas,/api/pixel,/api/events. - Reproducible Nix dev shell and a distroless, non-root container (< 20 MB).
- Task runner (go-task) exposing build, test, lint, security, container, and deploy actions.
- Multi-level tests: unit, integration (Testcontainers + real Redis), API contract (Hurl + OpenAPI), and load (k6).
- Supply-chain security: gitleaks (secrets), Syft (SBOM), Trivy (CVE scan).
- Git hooks (lefthook) and Conventional Commits enforcement.
- CI (GitHub Actions): quality, tests, and container build + scan; image
published to GHCR on push to
main. - Kubernetes deployment: Traefik load balancing, 3 replicas, HPA, and automatic HTTPS via Let’s Encrypt; Argo CD Application for GitOps.
- Admin dashboard at
/admin(enabled byADMIN_PASSWORD): runtime-tunable limits (rate limit/window — with an on/off switch, registration delay, token TTL, presence timings, and the SSE coalescing window for server fan-out), an editable canvas size (8–512 px per side, which resets the canvas), read-only maintenance mode (with a custom banner message), canvas reset, an editable preset colour palette (optionally hide the colour picker or enforce the palette server-side so off-palette colours are rejected by the API), a site-wide announcement banner, an open/close registration switch, and live stats. Settings persist in Redis and propagate to every replica via aconfig:eventspub/sub channel. Auth uses a constant-time password check and anHttpOnly,SameSite=Strictsession cookie. - Players & leaderboard: each visitor registers a unique pseudo that is
bound to their token server-side, so the leaderboard credit is derived from
the token (not the paint request) and can’t be spoofed.
POST /registertakes the pseudo (409 if taken);GET /api/leaderboardreturns the top-10, also pushed live as a namedleaderboardSSE event. Admins can remove a player (revokes the token, frees the pseudo, drops them from the leaderboard) and revoke legacy tokens that carry no bound pseudo. - Territory / ownership: each pixel remembers its current owner, so
GET /api/ownershipreports how many pixels each player owns and their % share of the canvas. Ownership transfers when a pixel is overwritten (unlike the cumulative leaderboard) and resets with the canvas. - Documentation: README with diagrams, CONTRIBUTING, per-directory READMEs, AGENTS.md, and Architecture Decision Records.