The release notes were passed through a step output and interpolated straight
into the gh command. Commit messages here routinely contain backticks, so the
shell evaluated them as command substitution — which failed the first release
outright, and would let a commit message run arbitrary code in CI.
Notes are written to a file and passed with --notes-file; nothing from the
commit reaches a shell as code.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dashboard sends people to the releases page to see what changed. That page
held a single entry from five months earlier while the product shipped several
times a day, which tells a visitor the project is abandoned — raised on #119,
and the more useful half of that thread.
A release is now cut whenever the shipped version changes, tagged to match the
container image so the thing you are running and the notes you are reading carry
the same number. Notes are the commit body rather than a generated list of
subjects, because the commit messages here already explain what changed and why.
Betas are marked pre-release so nobody is pointed at one by accident.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The default cache glob matches requirements.txt or pyproject.toml, and the
documentation toolchain is deliberately kept separate from the application's —
so setup-python found nothing and failed the job before it could build.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes#112.
Source stays `docs/*.md`, rendered by MkDocs-Material and published to GitHub
Pages by Actions. Same repository on purpose: documentation drifts, and
co-location is the only thing that reliably prevents it — the pull request that
changes behaviour changes the description, and review sees both. A separate repo
would double the ceremony and guarantee divergence.
The markdown remains readable when browsing the repository, so the site renders
those files rather than replacing them.
**Versioned from the start**, using mike. Images are published to GHCR and this
project ships betas most days, so readers are routinely running something other
than the newest build; a single "latest" would confidently describe a release
they do not have. Versions follow the product rather than the docs, so the site
and the running image carry the same number.
**Content came first, and it had to.** The docs were eleven days and sixteen
releases stale — SSH keys, bulk commands, the data-cap SMS, poller health and the
timezone setting appeared in exactly zero files between them. Publishing that
would have given documentation missing five of the most consequential features a
polished, authoritative home, and a well-presented wrong answer is worse than an
obviously rough one. Four pages now cover them:
- cellular.md — signal grading, carrier aggregation, the uplink anchor,
observed bands, and the data-cap SMS including why it fires early and how to
calibrate the margin
- ssh-keys.md — leading with what installing a key costs, because RouterOS
disables password SSH for that user and the database becomes load-bearing
- commands.md — why waves exist, and why reading output matters when RouterOS
reports failure in text rather than an exit code
- scaling.md — headroom, why it is insufficient on its own, and every tunable
Pull requests build the site with `--strict`, which turns warnings into errors.
That is the point of running it there at all, and it earned itself immediately:
the first build failed on two links to the project README, which sits outside the
docs tree. They are absolute now, so they resolve both in the rendered site and
when reading the markdown on GitHub.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Security Audit job failed because new advisories dropped against existing
deps and `npm audit --audit-level=high` audits the whole tree (incl. dev/build
tooling that isn't shipped).
- Switch both audit steps to `audit-ci --high --skip-dev`, so the gate reflects
what's actually deployed (backend ships --omit=dev; frontend ships a static
build) while still failing on real high/critical production advisories.
- Allowlist GHSA-qwww-vcr4-c8h2 (React Router "RSC Mode CSRF") for the frontend:
this is a client-side SPA using BrowserRouter — RSC mode is not used — and
there is no forward-compatible fix for react-router-dom 7.x (only a downgrade).
- `npm audit fix` on both lockfiles to patch non-breaking transitive dev
advisories (js-yaml, postcss, brace-expansion, minimatch). No direct/prod
dependency versions changed; app runtime is unchanged.
Verified: backend 114 tests pass, frontend builds, and audit-ci passes for both
projects locally.
Address the open CodeQL code-scanning alerts:
- guestWifi voucher codes now use rejection sampling so the character
distribution is uniform (fixes js/biased-cryptographic-random).
- Scheduled-report recipient validation replaced the backtracking email regex
with a linear split-based check (fixes js/polynomial-redos).
- Add .github/codeql/codeql-config.yml excluding js/missing-rate-limiting:
rate limiting is applied globally via app.use('/api', rateLimitRedis) plus
per-route limiters, which CodeQL can't attribute to individual handlers, so
that rule fired ~38 false positives.
Remaining alerts are dismissed as accepted/false-positive (RouterOS MD5 API
login and self-signed cert acceptance are protocol-mandated; log sites already
strip CRLF).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add docker/setup-qemu-action and platforms: linux/amd64,linux/arm64 to
both the backend and nginx build-push steps so GHCR images run natively
on arm64 (ARMv8) hosts instead of amd64-only. Refs #56.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
docker/build-push-action@v6 renamed the parameter from 'dockerfile' to
'file'. The backend succeeded by luck (default Dockerfile matched), but
nginx failed because the Dockerfile isn't in the root context directory.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a GitHub Actions workflow that builds and pushes backend and nginx
images to GitHub Container Registry on every merge to main and on
releases. Adds docker-compose.ghcr.yml for end users who want to deploy
without cloning the repo, and updates the README with Quick Deploy
instructions alongside the existing build-from-source guide.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SECURITY.md with vulnerability disclosure policy and self-hosting guidance
- Dependabot config for weekly dependency updates (npm, Docker, Actions)
- ESLint v9 flat config for frontend and backend, with eslint-plugin-security
- GitHub Actions CI: lint, typecheck, build, and npm audit on every push/PR
- CodeQL workflow for automated security scanning (runs on push and weekly)