site generator: commands/*.md with front matter -> k8s/site/
site / generated-is-current (push) Failing after 36s

- build/build.mjs: one-file-per-command content (filename = command,
  underscores = spaces; help/suggestion/params/prefix/text/hidden in
  front matter; ${args} ${date} ${time} ${ls} ${help} resolved at runtime)
- generates index.html (banner + chips), commands.json (the single data
  source), hello.txt for curl, style.css via tailwind v4
- src/site.js is now dispatch-only over commands.json; typewriter kept
- src/telnet.py: stdlib telnet shell sharing commands.json
- k8s: python telnet sidecar in the pod + LoadBalancer service 23->2323
- .gitea/workflows/site.yml fails CI when k8s/site/ is not the built output
This commit is contained in:
momme
2026-10-14 01:14:28 +02:00
parent 5852efafd7
commit 8d33460580
47 changed files with 2773 additions and 424 deletions
+63 -59
View File
@@ -1,72 +1,76 @@
# momme.world
Personal site at [momme.world](https://momme.world) — a terminal-style page
that behaves like a small shell (`whoami`, `projects`, `social`, …), plus a
plain-text rendering for `curl momme.world`.
that behaves like a small shell, a plain-text rendering for
`curl momme.world`, and a real telnet shell for the tailnet.
## Layout
## How it is put together
```
k8s/
kustomization.yaml the whole deployment: generated ConfigMaps + refs
deployment.yaml one unprivileged nginx, content served from ConfigMaps
service.yaml ClusterIP, :80 -> :8080
site/ the actual website, served as-is
index.html markup + noscript fallback
style.css
site.js the terminal; ALL site content lives in COMMANDS here
nginx.conf mounted as /etc/nginx/conf.d/default.conf
hello.txt what curl gets
```
**`commands/*.md` is the content.** One file per command, front matter for
metadata, body for output. The filename is the command — underscores are
spaces:
There is deliberately **no build step**: no package.json, no bundler, no CI.
Edit a file under `k8s/site/`, merge to `main`, ArgoCD syncs it. The content
of the site is the repo — the kustomize `configMapGenerator` hashes the files
into their ConfigMap names, which rolls the Deployment automatically.
| file | command | notes |
|---|---|---|
| `help.md` | `help` | |
| `project_fpv.md` | `project fpv` | multi-word commands just work |
| `echo.md` | `echo hello world` | `params: <text>` + `${args}` in the body |
Front matter keys (all optional except the body):
| key | meaning |
|---|---|
| `name:` | override the filename-derived command name |
| `help:` | one-line description; shown in `help` (no help = listed by `ls`, not in `help`) |
| `params:` | argument placeholder shown in help, e.g. `<text>` |
| `prefix: true` | command swallows anything typed after its name |
| `suggestion: true` | rendered as a chip button under the terminal |
| `hidden: true` | works, but stays out of `help` |
| `text: true` | included in the plain-text page curl clients see |
Dynamic placeholders inside a body, resolved when the command runs:
`${args}` (the arguments), `${help}` (the full listing), `${ls}` (subcommand
names), `${date}`, `${time}`.
## Editing the site
- New terminal command: add one object to `COMMANDS` in `k8s/site/site.js`.
It appears in `help` and in tab-completion automatically. `label -> url`
lines become links.
- The noscript block in `index.html` mirrors the main facts by hand; keep it
in sync when changing `whoami`/`contact` content.
- `curl`-UA detection is the `$root_doc` map in `k8s/site/nginx.conf`.
## How it deploys
The `momme/infra` module `app-momme-world` owns the namespace, the TLS
certificate and the HTTPProxy, and creates an ArgoCD `Application` that points
at `k8s/` in this repo. Nothing in this repo talks to the cluster.
## Local preview
Any static server works, e.g.:
```
cd k8s/site && python -m http.server 8080
```bash
npm install
# edit commands/, src/index.html or src/input.css
npm run build # regenerates k8s/site/
git add -A && git commit && git push
```
`npm run build` (build/build.mjs) generates everything in `k8s/site/`:
the HTML (banner + chips injected), `commands.json` (the single data source
both the browser runtime and the telnet shell read), `hello.txt` for curl, and
`style.css` via Tailwind v4. **The generated files are committed on purpose** —
that is what ArgoCD serves — and CI fails if `k8s/site/` is out of sync with
the sources, so a forgotten build can never desync the site.
Styling is [Tailwind v4](https://tailwindcss.com): theme tokens live in
`src/input.css`, class names in the template and chips. `src/site.js` is the
browser runtime (terminal loop + typewriter), `src/telnet.py` the tailnet
shell — both are dispatch over `commands.json`, neither carries content.
## Deploy
`k8s/` is kustomize: one pod with nginx (site) and python (telnet shell), all
content as ConfigMaps, so a content change rolls the pod without any image
build. Deployed by ArgoCD from the `app-momme-world` module of the infra repo
(git.momme.cloud/momme/infra).
## Open decisions
Things left deliberately open for you:
1. **DNS** — `momme.world` currently has *no A record*. The zone lives in
Cloudflare (nameservers `fred/jessica.ns.cloudflare.com`), the same place
`momme.cloud` lives. An `A momme.world -> 31.18.25.47` (grey cloud, the
same public entry all `*.momme.cloud` services use) is all that's needed.
Also check that the Cloudflare API token behind the `cf` ClusterIssuer
(Infisical `/global`, `cloudflare_api_token`) is allowed to do DNS-01 in
the momme.world zone — if it's scoped to momme.cloud only, widen it.
2. **www redirect** — not implemented. If you want `www.momme.world` to work,
add a second DNS name + HTTPProxy route (infra PR can carry it).
3. **Analytics / visits** — none included. Self-hosted (Plausible) or none is
an easy later call; nothing in this repo blocks it.
4. **Blog later** — you mentioned a Markdown-driven blog could be nice
someday. The current no-build design can take it as far as "one static
page per post written in HTML"; as soon as you want real Markdown +
templates, the natural step is a generator (Lume like adb.sh, or
Hugo) plus a built image. Decision deferred, structure unaffected.
5. **Content facts** — taken from the old site's commands (work at
codeanker, projects list, socials). Say the word for anything outdated;
they all live in `COMMANDS` in `site.js`.
* **DNS** — `momme.world` still needs an A record (Cloudflare) pointing at the
cluster's public entry `31.18.25.47`; until then no certificate and no
public HTTPS. Also check that the `cf` ClusterIssuer's API token covers the
`momme.world` zone (it was created for `momme.cloud`).
* **telnet exposure** — currently tailnet-only: `momme-world-telnet` is a
LoadBalancer service that needs a one-line IP-pool entry in
`infra/sys-cilium/lb.tf` (pattern: `gitea-ssh`). Public port 23 would need a
TCPRoute on the njalla/Envoy funnel — doable, left open on purpose.
* The footer advertises `telnet momme.world 23`; if port 23 stays tailnet-only,
change it to the LB IP (or drop the hint).
* GitHub Pages still serves the old site at supermomme.github.io/momme.world —
archive or delete that repo once this one is live.