- shots.py crops to a region (clip) or to what one or more elements cover
(element, pad), per-shot viewport; capture.py writes the published sizes to
src/data/screenshots.json so the page reserves the right space.
- anonymize.py no longer empties secrets that netOrk compares with each other
(Wi-Fi keys on an SSID against the key read from the access point). Emptying
them invented passphrase "drift" that never existed; a keyed hash keeps equal
equal, reverses nothing, and its key lives for one run.
- scripts/check/site.py checks the built site in both languages at four widths:
sideways overflow, one h1, images with alt and size, console errors, requests
to other origins, links to unknown routes, old-URL redirects, language
detection, and word counts against the budgets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The homepage showed seven hand-built JSX imitations of the netOrk UI. They
are gone; every image is now a screenshot of netOrk v0.28.0 itself, taken
from an anonymized copy of a production database (scripts/demo) with
scripts/screenshots/capture.py and published as WebP (~630 KB for all eight).
- Hero: the device inventory. Walkthrough: device detail, VLANs, the Security
tab, the vulnerability triage queue (replacing the config-diff row), the
dashboard and service checks (new row 6). NIS2: the audit log, filtered to
what people did.
- Copy follows the images: row 3 describes the security assessment, row 4 the
triage queue; row 2 no longer claims corrections are always automatic;
18 widgets. Alt texts in both languages.
- Also fixed on the homepage: the NIS2 teaser for Art. 21 (2e) and the
container list of a deployment (three worker pools, plus Flower, registry,
APT cache and the Signal gateway).
- Demo tooling hardened on the real dump: secrets inside JSON (Wi-Fi keys),
reverse DNS zones, glued identifiers, tens of thousands of CrowdSec
addresses, a schema newer than the release (anonymize, then downgrade),
MFA-enforcing roles, and click steps for view filters.
- DESIGN.md: real screenshots only. PAGES.md: the six rows as they are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The site has shown hand-built JSX mockups of the UI so far. This adds the
tooling to replace them with screenshots of the real application:
- scripts/demo/up.sh restores a pg_dump of a production database into a
local Postgres and starts netOrk (a pinned release, default v0.28.0) with
only the API and the UI: no worker, no beat, no Redis, a random encryption
key. Nothing polls and nothing can reach a device.
- scripts/demo/anonymize.py rewrites every text, JSON and address column of
every table: domains to example.demo, private IPv4 per /16 with the host
part kept, public addresses into the documentation ranges, MACs with the
vendor prefix kept, e-mail addresses and configured names. Secrets are
emptied by column name, one admin "netork" is left. It refuses non-local
databases and ends with a leak report. The real-to-demo name map lives
outside the repo.
- scripts/screenshots/capture.py drives headless Chromium through a
declarative list of pages, logs in to the demo copy by itself, and aborts
every non-GET API request, so taking screenshots cannot change anything.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image moves off registry.netork.io. That registry is plain registry:2 with
htpasswd auth, which knows nothing about repositories: every account that can log
in reads and writes everything on it, including the accounts issued to customer
instances. Verified -- a customer server's credentials list the whole catalogue.
It keeps the images those instances are meant to pull; the marketing site is not
one of them. Gitea scopes packages to their owning account, and no customer has
one. netOrk #172.
Login uses a REGISTRY_TOKEN secret (a Gitea token with write:package). The token
Actions injects per run does not work here -- the package registry rejects it
with a bare "unauthorized", which is a confusing way to spend an afternoon.
The deploy job is removed rather than migrated, because it had quietly stopped
being correct. It ran `docker run` against whatever runner picked the job up,
which worked while exactly one runner existed. There are now several --
netork-runner-12 on .12, netork-runner-13 on .13, plus the original
netork-runner -- and none of them is on 10.7.224.11, where this site runs and
where the proxy-net it attaches to lives. The next push would have started a
second website container on the wrong host and reported success while netork.io
went on serving the old one. Nothing had failed yet; the last deploy was
2026-07-17, back when the pool was one runner.
scripts/deploy.sh replaces it: it names the target, pulls before it removes
anything, compares the running container's image id against what was pulled, and
finishes by checking that netork.io actually answers 200.
Push-to-deploy can come back by registering a runner on .11 with a label of its
own and pinning `runs-on:` to it, or by giving CI an ssh key. Both decide where a
credential lives, so neither was decided here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>