Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contributing to Pixelflux

This guide covers everything you need to develop, test, and ship Pixelflux: the dev environment, the git workflow, and how to run each kind of test.

Table of contents

Prerequisites

  • Nix with flakes enabled
  • A container runtime (Docker or Podman) — needed for integration tests and the container image

Nothing else has to be installed: the Nix dev shell provides Rust, the task runner, all linters, the security scanners, and the test tooling at pinned versions.

Getting started

git clone git@github.com:Vallsp/PixelFlux.git
cd PixelFlux

nix develop            # enter the reproducible dev shell (or: direnv allow)
task lock              # generate Cargo.lock (first time only)
task hooks:install     # install the git hooks
task run               # run the server -> http://localhost:3000

Run task with no arguments at any time to list every available task.

Git workflow

Branches

  • main is always releasable; CI runs on every push and pull request.
  • Do your work on a short-lived branch named after the change, e.g. feat/eraser-tool, fix/redis-reconnect, docs/contributing.
  • Keep branches small and focused; rebase on main before opening a PR.
git switch -c feat/eraser-tool
# ...work...
git push -u origin feat/eraser-tool

Commit messages

We follow Conventional Commits. The commit-msg hook rejects any message that doesn’t match. Format:

<type>(<optional scope>): <description>

Allowed types:

TypeWhen to use it
featA new feature
fixA bug fix
docsDocumentation only
styleFormatting, no code change
refactorCode change that neither fixes a bug nor adds a feature
perfA performance improvement
testAdding or fixing tests
buildBuild system or dependencies
ciCI configuration
choreMaintenance, tooling
revertReverting a previous commit

Examples:

feat: add an eraser tool to the canvas
fix(redis): reconnect the pub/sub subscriber on drop
ci: implement sbom task with Syft

Git hooks

Installed with task hooks:install (managed by lefthook):

HookWhat runs
pre-commitformat staged files (treefmt), clippy -D warnings, secret scan on staged changes (gitleaks)
commit-msgenforce Conventional Commits
pre-pushunit tests + a release build

To bypass them in an emergency: git commit --no-verify (avoid on shared branches — CI will still enforce the same checks).

Pull requests

  1. Make sure the full gate passes locally: task check (lint + secrets + tests).
  2. Push your branch and open a PR against main.
  3. CI must be green before merging (it runs the same checks plus the container build and CVE scan).

Everyday commands

task run                # run the server on :3000
task build              # debug build
task fmt                # auto-format every file type (treefmt)
task check              # full local gate: lint + secrets + tests

For the shared, persisted canvas (mirrors production), run Redis alongside:

docker run -d -p 6379:6379 redis:7-alpine
REDIS_URL=redis://localhost:6379 task run

Running the tests

There are four levels of tests. Unit tests run anywhere; the others need a container runtime and/or a running server.

LevelCommandWhat it doesNeeds
Unittask testCanvas logic and routes exercised in-memorynothing
Integrationtask test:integrationA pixel painted via one instance is read back from a real Redis (Testcontainers)Docker/Podman
API contracttask test:apiBoots the server, validates it against api/openapi.yaml with Hurl
Load / benchmarktask benchk6 reads the canvas and paints random pixels under load (p95 < 200 ms, < 1% errors)

task test:api and task bench start the server themselves and stop it when they finish, so you don’t need a server running beforehand.

Quality, formatting & security

task fmt                # format Rust, TOML, Nix, shell, Markdown, YAML, JSON (treefmt)
task lint               # treefmt --fail-on-change + clippy + yamllint + actionlint + markdownlint
task secrets            # scan the whole repo for leaked credentials (gitleaks)

task lint is what CI runs; task fmt fixes most of what it would flag.

Building the container

The image is built entirely by Nix — a distroless static binary, non-root, under 20 MB.

task container          # build the image with Nix
task container:load     # build and load it into Docker
task container:size     # print the size and fail if it exceeds 20 MB
task container:inspect  # explore the layers with dive
task sbom               # generate an SBOM (Syft) -> sbom.json
task cve                # scan the image for CVEs (Trivy), fails on HIGH/CRITICAL

Deploying

Kubernetes + Traefik on a single-node k3s host. See the Deploy section of the README for the full flow; in short:

task deploy:k3s-install                  # once: install k3s + Traefik
task deploy                              # build, import, apply the app
DOMAIN=your.domain.com task deploy:ingress              # expose over HTTP
# or HTTPS (Let's Encrypt):
DOMAIN=your.domain.com ACME_EMAIL=you@domain.com task deploy:tls

deploy:ingress (HTTP) and deploy:tls (HTTPS) define the same route, so the last one applied wins — don’t mix them. After enabling HTTPS, use task deploy:restart (not task deploy) for code changes so the TLS route is preserved.

Continuous integration

.github/workflows/ci.yml runs three jobs, all inside nix develop so CI and local development share the same toolchain:

JobChecks
qualitylint, format check, secret scan
testbuild, unit tests, integration tests (Testcontainers)
containerbuild the distroless image, enforce < 20 MB, SBOM, Trivy CVE scan

The build fails if any check fails, so the server-side pipeline catches issues even when local git hooks are skipped.