Files
roro9stack/site
twislaandClaude Opus 5.5 4404dd9380 VPN: a WireGuard tunnel (#8)
The device joins a WireGuard network over whatever Wi-Fi it is on: one
peer, IPv4. A client's .conf is imported from the card (/vpn/wg0.conf) and
kept in the device's settings, private key included, never shown; Settings
offers to delete the file. A switch brings the tunnel up until the next
restart, "Start with Wi-Fi" every time; it waits for the clock, which a
handshake needs. VPN shows in the Status Bar.

The protocol is esphome/wireguard 0.4.8. It calls lwIP without lwIP's lock,
which this framework checks: every call into it is made with the lock held.

What goes through the tunnel is everything (AllowedIPs 0.0.0.0/0) or the
one subnet the device's tunnel address is in: lwIP routes by an
interface's subnet or by default, nothing finer. The import says how many
ranges it can't reach.

Checked against a test peer in both directions and against a real server,
with a configuration uploaded from a phone (docs/milestones/N1.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-08 01:49:47 +02:00
..
2026-10-08 01:49:47 +02:00
2026-10-08 01:49:47 +02:00
2026-10-08 01:49:47 +02:00

The roro9stack site

Source of https://roro9stack.net (docs/milestones/W1.md): a Zola site. The home page, an Install page with a browser flasher, and the list of releases so far; the user guide, how-tos, FAQ and developer docs come next.

config.toml           base_url, the repository and API addresses
content/              the pages (Markdown, with their template named in the front matter)
templates/            base, home, install, downloads, 404; illustrations/ is generated
data/                 the App cards and the screenshots' captions
static/               css, js, fonts (self-hosted), img, screens (real screenshots), vendor/esp-web-tools
tools/                make_illustrations.py, check_site.py

Build and look

docker run --rm -u "$(id -u):$(id -g)" -v "$PWD:/repo" -w /repo/site ghcr.io/getzola/zola:v0.22.0 build   # writes ./public
docker run --rm -u "$(id -u):$(id -g)" -p 1111:1111 -v "$PWD:/repo" -w /repo/site ghcr.io/getzola/zola:v0.22.0 serve --interface 0.0.0.0
python3 site/tools/check_site.py public    # what the pages promise, kept

Or zola build with a Zola of your own, in site/ (or zola --root site build from the root). The output goes to public/ at the root of the repository, not into site/: output_dir in config.toml, and git ignores that directory. The build reads the latest release and the list of releases from the Gitea API (load_data); if the server can't be reached, the pages say so instead of failing.

Publishing

The web server pulls main and runs zola build, as for the blog. CI (.gitea/workflows/site.yml) builds the site and runs the checks when site/, docs/, README.md or CONTEXT.md change; the firmware workflow skips a change that touches only those.

Things to know

  • The Install page needs Caddy's help. Gitea's release downloads carry no CORS header, so the page asks the API from the browser only if Caddy, in front of Gitea, allows this origin:

    @releases {
        method GET HEAD
        path /twisla/roro9stack/releases/download/* /api/v1/repos/twisla/roro9stack/releases*
    }
    header @releases Access-Control-Allow-Origin "https://roro9stack.net"
    header @releases Vary Origin
    

    Without it the page says it can't reach the release server and points to the esptool steps.

  • No third-party requests. Fonts and the flasher library are served from here; tools/check_site.py fails the build if a page loads anything from another origin.

  • Illustrations. templates/illustrations/*.html are generated by tools/make_illustrations.py; run it again rather than editing them.

  • Updating the flasher library: see static/vendor/esp-web-tools/README.txt.

  • Fonts are DM Mono and Hanken Grotesk, under the SIL Open Font License (the licences are in static/fonts/).