Public Access
/dev/ has Debug Builds and the Debug Console (builds and the token, the console and its protocol, files and screenshots, driving the UI, crashes and Safe Mode, the command reference), Build, test and release (including how an update works), the architecture decisions and the milestone plans. Generated from the repository by site/tools/gen_dev_docs.py: the ADRs, the milestones, the README's sections, and the command reference, read from the firmware's own `help` text. The pages are committed (Zola cannot read outside its folder); the Site workflow checks they are current, and now also runs when src/main.cpp changes. M0, M1 and CONTEXT.md are not published. README: the gnss commands that the table lacked. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
128 lines
17 KiB
Markdown
128 lines
17 KiB
Markdown
# W1: Website
|
|
|
|
**Status:** phases 1 to 3 (home, Install and Downloads; the user guide; how-tos and the FAQ) and the devlog are live at roro9stack.net; phase 4 (the developer docs) is in a pull request. Issue #12.
|
|
|
|
**Goal:** a public home for the project at **roro9stack.net**, separate from the blog (stories) and from Gitea (developers): what it is, how to install it, how to use each App, and the docs.
|
|
|
|
The home page was designed on a canvas in a Claude chat (a dark and a light theme, built on the device's own 256-colour palette, pixel-notched corners, DM Mono and Hanken Grotesk). It is the starting point, not the final copy: it has to say only what the firmware does today.
|
|
|
|
## What was found while planning (2026-10-06)
|
|
|
|
- `roro9stack.net` already points at the server that hosts Gitea. Plain HTTP redirects to HTTPS; HTTPS has no certificate yet, which is the server's side to set up.
|
|
- **Release downloads from Gitea carry no CORS header,** so a browser can't fetch the factory image from another origin as things are. Gitea is behind Caddy, which can add the header (below).
|
|
- Zola can read JSON from a URL at build time (`load_data`), so the home page's "latest version" can come from the Gitea API.
|
|
- There is no Gitea wiki: the design's "Wiki" links would 404.
|
|
- The blog is published by pulling its repository on the web server and running `zola build`. The site does the same.
|
|
|
|
## Decisions (design round 2026-10-06)
|
|
|
|
| # | Decision |
|
|
|---|---|
|
|
| Q175 | The site lives in this repository, in `site/`, so the documentation is built from `docs/`, `CONTEXT.md` and the README instead of being copied. |
|
|
| Q176 | **Zola,** like the blog. The design becomes a template, its tokens CSS custom properties. Dark and light follow the visitor's setting, with a visible switch. No JavaScript except the flasher's. |
|
|
| Q177 | Domain: **roro9stack.net.** The blog stays at experiments.twis.la. |
|
|
| Q178 | **Publishing is the blog's way:** the web server pulls `main` and runs `zola build`; that part is the maintainer's. Changes reach `main` through pull requests as everywhere. **CI is split:** a dedicated `site` job builds the site (`zola build`) when `site/`, `docs/`, `README.md` or `CONTEXT.md` change, and the firmware tests and builds skip a change that touches nothing else. A change that touches both runs both. |
|
|
| Q179 | Phases, each its own pull request: **1.** the CI split, the home page, an Install page with the browser flasher, downloads and the changelog. **2.** a user guide page per App. **3.** how-tos and the FAQ. **4.** developer docs generated from the repository. |
|
|
| Q180 | **A browser flasher** (ESP Web Tools), **without copying the firmware.** Caddy, in front of Gitea, adds `Access-Control-Allow-Origin: https://roro9stack.net` (and `Vary: Origin`) to GET and HEAD on `/twisla/roro9stack/releases/download/*` and `/api/v1/repos/twisla/roro9stack/releases*`: both are public already. The Install page asks the API for the latest release in the browser, finds the asset ending `-factory.bin`, and gives ESP Web Tools a manifest built on the spot, so it offers a new release as soon as it exists, with no rebuild. The library is **vendored** into `site/static/` (Apache-2.0), not loaded from a CDN. The file's SHA-256 is shown on the page. Chrome or Edge on a desktop only; other browsers, and visitors without JavaScript, get the `esptool` steps on the same page. |
|
|
| Q181 | Docs for the latest version only. The changelog is the Gitea releases, read at build time. |
|
|
| Q182 | English only. |
|
|
| Q183 | The FAQ starts from real questions: the README, and issues labelled `kind/docs`. |
|
|
| Q184 | Fonts are **self-hosted** (no request to a third party). The hero keeps the design's illustrations, labelled as illustrations, and a section of **real device screenshots** is added. |
|
|
| Q185 | **The site says only what the firmware does today.** Planned features are marked as planned, with their milestone. The mesh messenger is **planned**: the LoRa Scanner listens, nothing is sent. |
|
|
| Q186 | Left out, each with its issue: a Gemini capsule mirror (#57), French (#58), docs per version (#59), search (#60). |
|
|
| Q187 | The site has no version of its own. Contact is **contact@roro9stack.net,** and the issue tracker. |
|
|
|
|
## The design, reviewed
|
|
|
|
Kept as designed: the layout, the tokens, the nine App cards (their facts check out against the code: Probation 3 minutes, Safe Mode after 3 crashes, 60 seconds of typing before an update restarts the device).
|
|
|
|
Changed before it ships:
|
|
|
|
- **Install, not Download, is the first action.** Downloads are for developers; a visitor wants to try it.
|
|
- **A "what you need" strip:** Cardputer ADV, the Cap LoRa-1262 (only the radio needs it), a microSD card, Wi-Fi. And a plain status line: the version, and what isn't there yet.
|
|
- **The mesh card and the hero** no longer promise sending and reading mesh messages.
|
|
- **The latest version is read from the API,** not typed.
|
|
- **"Wiki" is replaced by Docs.** The updates section gains what v0.11.0 added: the device installs releases from the project's server itself.
|
|
- **An independence line:** not affiliated with or endorsed by M5Stack or Meshtastic.
|
|
- **No cookies, no analytics, no third-party requests,** said on the page (fonts self-hosted).
|
|
- **The keyboard focus ring** is invisible on the notched buttons: `clip-path` clips an outline. Another way to show focus is needed.
|
|
- **The wordmark SVGs** carry an embedded C2PA content-credentials block: stripped from the site's copies.
|
|
|
|
## Done when (phase 1)
|
|
|
|
- Pushing a change under `site/` runs the `site` job and not the firmware tests; a firmware change runs the firmware jobs and not the site's.
|
|
- The home page renders in both themes, at phone width, with the keyboard, and says nothing the firmware doesn't do.
|
|
- The latest version on the page is the latest release.
|
|
- The browser flasher installs the latest release on a Cardputer ADV from Chrome (tried by hand), the page shows the file's SHA-256, and the `esptool` steps are on the same page.
|
|
- A release published after the site was built is the one the Install page offers.
|
|
- The home and Downloads pages make no request to another origin, and the Install page only asks git.twis.la.
|
|
|
|
## Work breakdown
|
|
|
|
1. **CI split:** a `site` workflow, path filters on the firmware workflow.
|
|
2. **The skeleton:** `site/` with the tokens, fonts, base template and the theme switch.
|
|
3. **The home page,** from the design, with the changes above.
|
|
4. **Install and downloads:** the flasher with its manifest built in the page, the `esptool` steps, the changelog. Needs the Caddy headers on the Gitea host (the maintainer's side); the page is tested against them once they're in.
|
|
5. **Checks,** recorded here.
|
|
|
|
## As built (phase 1)
|
|
|
|
- **CI is split.** `ci.yml` (the firmware) has `paths-ignore: site/**, docs/**, README.md, CONTEXT.md` on pushes to `main` and on pull requests. `site.yml` runs `zola check` and `zola build` with a Zola pinned by its checksum, then `site/tools/check_site.py`, when those files change. Gitea's own source (v1.24, read, not run against the 1.27 server) shows that path filters count as matched for tag pushes, so a tag still releases. A change that touches both runs both.
|
|
- **The build output** goes to `public/` at the root of the repository, not into `site/`: `output_dir = "../public"` in `site/config.toml`, so `zola build` in `site/` and `zola --root site build` from the root agree, and git ignores `/public/`.
|
|
- **The site** is in `site/`: `config.toml`, templates (base, home, install, downloads, 404), `data/` for the App cards and the screenshots' captions, `static/` (stylesheet, theme switch, fonts, wordmark, icons, real screenshots, the vendored flasher). `site/README.md` says how to build it and what the server needs.
|
|
- **The home page** follows the design. Changed from it: the hero and the mesh card promise nothing that isn't built (the mesh messenger is a "planned" card), an Install button first, a status box, a "what you need" row, a section of real screenshots, the updates section says the device installs releases itself, an independence line and a statement about cookies and third-party requests in the footer, the latest version read from the Gitea API at build time, and the nav's Wiki replaced. The two hero drawings are generated by `site/tools/make_illustrations.py` (a port of the design's scripted shapes) as inline SVG.
|
|
- **The focus ring.** `clip-path` clips outlines, so a focused notched control drops its notches and shows square corners and its ring.
|
|
- **The Install page** asks the API for the latest release in the browser, builds the ESP Web Tools manifest as a blob, shows the version, size and SHA-256, and only ever hands the flasher a download from the project's own server for this repository. The flasher library (ESP Web Tools 10.4.0, Apache-2.0) is vendored, trimmed to the ESP32-S3. Fonts (DM Mono, Hanken Grotesk, SIL OFL) are self-hosted.
|
|
- **The Downloads page** lists the last 30 releases with their files, read at build time.
|
|
- **The wordmark SVGs** from the design carried an embedded C2PA content-credentials block; it is removed from the site's copies.
|
|
|
|
## As built (phase 2, the user guide)
|
|
|
|
- **`/guide/`** is a section of 11 pages, `site/content/guide/`, each with `template` from the section's `page_template` and an order from `weight`: the basics (keys, Launcher, Status Bar, first start, the card), then one page per App (LoRa Scanner, GNSS, Gemini, IRC, Wi-Fi tools, Notes, Storage, System), Settings and Updates. Pages with real screenshots list them in `extra.screens`, looked up in `data/screens.toml`.
|
|
- **Facts come from the README, the milestone documents and the Apps' own source** (key handlers, labels, the Status Bar's drawing code), not from memory. Some wording was corrected against the source while writing: the Track folder is `/gnss/tracks`, the reasons a Track won't start, what the Status Bar shows.
|
|
- **Not covered:** the mesh messenger (planned), the debug console and Debug Builds beyond a pointer to the README. How-tos and the FAQ are phase 3.
|
|
|
|
## As built (the devlog)
|
|
|
|
Not one of the planned phases: the blog's seven roro9stack posts, imported into `site/content/devlog/` and shown in the site's own style, with the **Blog** link in the navigation and the footer replaced by **Devlog**. The posts' text, tone and structure are unchanged; what changed:
|
|
|
|
- **Links:** the posts' links to each other point to `/devlog/<same name>/`, and one link to an unpublished work-in-progress post became plain text. Each post keeps its old directory name, so the old URL `/<name>/` maps to `/devlog/<name>/`.
|
|
- **The posts' parts** (the sign, the cast, the steps, asides, folded sections, diagrams, captions) are shortcodes in `site/templates/shortcodes/`, restyled in `static/css/devlog.css`: the site's palette, notched boxes, DM Mono and Hanken Grotesk. The two older posts about other subjects (a vinyl remote, a ZFS rescue) stay on the blog.
|
|
- **The 17 diagrams are inline SVG**, and carried `<style>` blocks and `style` attributes that the site's Content-Security-Policy refuses. Their rules moved to `static/css/devlog-diagrams.css` (one block per diagram, plus colour classes for what the attributes did), and a diagram's minimum width is a class, not a style attribute. The Caddy policy needs no change.
|
|
- **The diagrams' four colours** (red, green, yellow, accent) are defined for `.devlog` on the site's RGB332 grid, one value for each theme.
|
|
- **Links between posts:** every post's reference to another ("the last post", "the first post", the milestone lists) is a link to it. `check_site.py` now also checks every link inside the site, and its #fragment: a broken one fails the Site job. External links are checked by `zola check` run by hand (without `--skip-external-links`, which CI uses); its only complaints today are line-range and heading anchors on Gitea, which Gitea resolves in the browser.
|
|
- **An Atom feed** at `/devlog/atom.xml`, linked from every devlog page.
|
|
- **Checked** in Chromium with the production CSP applied to every response: the index and the seven posts, at 1100 and 390 px, no policy violation, no broken image, no sideways scroll; `check_site.py` 24 pages, 0 problems.
|
|
|
|
## Checks (2026-10-06)
|
|
|
|
| Check | Result |
|
|
|---|---|
|
|
| The Site workflow's own commands, in a clean container with the pinned Zola | `zola check` clean, build and page checks pass |
|
|
| `tools/check_site.py` | 4 pages, 0 problems: titles, descriptions, a language, every image with alt text, every local file referenced exists, nothing loaded from another origin |
|
|
| Browser tests (Chromium, 16 checks) | All pass: no request to another origin from the home, Downloads and 404 pages; no horizontal scroll at 1280 and 390 px; a visible focus ring on a notched button; the Install page reads the latest release, shows its version, size and SHA-256, builds a blob manifest naming an ESP32-S3 factory image at offset 0, and loads only git.twis.la; a download on another host is refused; the real server (no CORS header today) makes the page fall back to the esptool steps |
|
|
| The pages looked at | Home in dark and light, at desktop and phone width; Install; Downloads |
|
|
| `esptool` against the real v0.11.0 factory image | An ESP32-S3 image, bootloader at 0x0, partition table at 0x8000: flashing at offset 0 is right. The command's syntax was checked, not a flash |
|
|
|
|
**Not checked:**
|
|
- **Flashing a real Cardputer from Chrome.** It needs the device on a machine with a browser; the page's flasher logic is tested, the flashing itself isn't.
|
|
- **Caddy's headers** on the real server (not applied yet), and **HTTPS on roro9stack.net** (the name resolves, the certificate isn't there).
|
|
- **The CI split on a change that touches only `site/` or `docs/`.** This pull request touches both the workflows and the site, so it runs both; the first docs-only pull request will show it.
|
|
- Firefox and Safari rendering, screen readers, and a printed page.
|
|
- The unverified wording in the Install text is kept to what's known: nothing about how long flashing takes, or what the screen shows in download mode.
|
|
|
|
## As built (phase 3, how-tos and the FAQ)
|
|
|
|
- **`/howto/`** has eight short recipes: when flashing fails, find your files on the SD card, install an update from the card, use a network without DHCP, record a Track, capture LoRa packets for Wireshark, read Gemini pages offline, and what to do when a connection says "not enough memory". **`/faq/`** is one page of questions with a list at the top. Both use the guide's templates (`guide-index.html`, `guide-page.html`, now generic: the page's parent section gives the eyebrow, the title and the pager).
|
|
- **The FAQ starts from the README** and from the problems the project met (Q183): the flash troubles and the memory limit are the two that were hit most. The issues labelled `kind/docs` turned out to be design rounds for the mesh, not user questions, so they gave nothing to answer.
|
|
- **Every step comes from the README, the milestone documents or the Apps' source.** The privacy answer says plainly that the device contacts the project's server once a day for the update check (on by default, one switch to turn it off).
|
|
- **Linked from the guide's index,** not the navigation, which stays short.
|
|
|
|
## As built (phase 4, the developer docs)
|
|
|
|
- **`/dev/`** has four sections: **Debug Builds and the Debug Console** (first, and the longest: Debug Builds, the Console and its protocol, files and screenshots, driving the UI, crashes and Safe Mode, and the command reference), **Build, test and release** (the README's build, CI and flash sections, and how an update works, with the update file, the four ways in and Probation drawn), **Decisions** (the ADRs) and **Milestones** (the plans).
|
|
- **Generated from the repository, not copied by hand:** `site/tools/gen_dev_docs.py` writes the ADR pages, the milestone pages, the README's sections, and the command reference, which is read from the firmware's own `help` text in `src/main.cpp` and then the README's table of what each command does. Zola can't read outside its own folder (not even through a symlink), so the generated pages are **committed**, and the Site workflow runs `gen_dev_docs.py --check` and fails when one is out of date; it now also runs when `src/main.cpp` changes, because the command list lives there. The server's `pull; zola build` is unchanged.
|
|
- **Left out on purpose:** the M0 and M1 milestone documents and `CONTEXT.md` (the glossary) describe Wi-Fi monitoring, which the site does not publish. They stay in the repository.
|
|
- **The Debug Console pages were written against the source and the live console:** the protocol (the token line, the banner, the 4 KB backlog, one client, 8 queued commands, 240-byte lines, `denied` after a second) and the replies shown were checked on a Debug Build, v0.11.0-3, over Wi-Fi. Not run: `crash abort`, `crash wdt` and Safe Mode, which are described from ADR 0005 and the code.
|
|
- **Found while writing it:** the README's table lacked the `gnss` commands (rows added); piping commands into `rdbg.py` returns before the replies unless the input stays open (documented, not changed); `update install` on a Debug Build needs `force` (documented).
|