- 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
momme.world
Personal site at momme.world — a terminal-style page
that behaves like a small shell, a plain-text rendering for
curl momme.world, and a real telnet shell for the tailnet.
How it is put together
commands/*.md is the content. One file per command, front matter for
metadata, body for output. The filename is the command — underscores are
spaces:
| 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
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: 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
- DNS —
momme.worldstill needs an A record (Cloudflare) pointing at the cluster's public entry31.18.25.47; until then no certificate and no public HTTPS. Also check that thecfClusterIssuer's API token covers themomme.worldzone (it was created formomme.cloud). - telnet exposure — currently tailnet-only:
momme-world-telnetis a LoadBalancer service that needs a one-line IP-pool entry ininfra/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.