# Site Operations Operational guide for managing an htmx-ssr consumer site (L3 instance). All recipes run from the **site root** — the directory that contains `site/`. ## Runtime boundary The htmx-ssr binary separates what is compiled-in from what is read at runtime. This determines whether a change requires a restart, a rebuild, or nothing. | What you change | Binary effect | Action needed | |------------------------------------------|---------------|---------------------------------------| | Markdown content, NCL metadata | None | `just content` — no restart | | JSON content indexes (`site/r/`) | None | Already written by `just content` | | Knowledge graph (`content_graph.json`) | None | `just graph` — no restart | | Static assets (CSS, images) | None | Copy to `site/public/` — no restart | | Jinja templates (`.j2`) | None | `just templates` + server restart | | FTL strings (`site/i18n/locales/`) | None | Server restart only | | RBAC (`site/rbac.ncl`) | None | **Automatic hot-reload** — nothing | | Routes or menus (`site/config/routes.ncl`) | **Baked** | Binary rebuild (see §Routes & menus) | | New language (routes.ncl + site.ncl) | **Baked** | Binary rebuild (see §New language) | --- ## 1. Update content — preview locally Content = markdown posts, NCL metadata, and project/blog/recipe pages. ```sh # Edit markdown files: # site/content/{type}/{lang}/{category}/{slug}/index.md # site/content/{type}/{lang}/{category}/{slug}/index.ncl (metadata) # Regenerate JSON indexes: just content # all types, all langs just content type=blog # single type just content type=blog lang=en # single type + lang just content type=blog lang=en category=rust # single category (uses --category) # Watch mode (auto-regenerate on save): just content-watch just content-watch type=blog lang=en # Start server and open browser: just dev # http://localhost:3030 ``` Changes in `site/content/` are served directly — no server restart. The JSON indexes in `site/r/` are what the server reads; always run `just content` after editing NCL metadata or adding new files. --- ## 2. Update content including the knowledge graph The knowledge graph connects posts to ontology nodes and to each other. Requires `ONTOREF_NICKEL_IMPORT_PATH` and `CONTENT_GRAPH_IMPL_ONTOLOGY` in `.env`. ```sh just content # rebuild JSON indexes first just graph # rebuild content_graph.json just dev # preview ``` The graph is pre-built: `gen-content-graph --site .` writes `site/r/content_graph.json`. The server reads it as a static file — no restart after regeneration. --- ## 3. Publish content to production (no binary rebuild) Content changes do not need a new container image. Update the PV directly. **Kubernetes / docker-compose (PVC):** ```sh # Sync the JSON indexes to the running pod's PVC: kubectl cp site/r/ :/var/www/site/r/ # Sync source content (if the server reads markdown directly): kubectl cp site/content/ :/var/www/site/content/ # No rollout restart needed — server reads these on next request. ``` **Full PV resync (after template or FTL changes too):** ```sh kubectl cp site/ :/var/www/site/ kubectl rollout restart deployment/ ``` **New container image (when binary was rebuilt):** Run `just build-push-remote` from `website-htmx-rustelo/code/`. This builds a new image via lian-build and pushes it to the registry. Deployment manifests reference the new tag. --- ## 4. Update templates — preview locally and publish Templates are Jinja2 (`.j2`) files loaded at server startup from `HTMX_TEMPLATE_PATH`. ```sh # Edit source templates in the L2 project: # website-htmx-rustelo/code/crates/pages_htmx/templates/pages/{slug}.j2 # website-htmx-rustelo/code/crates/pages_htmx/templates/partials/{name}.j2 # Reassemble htmx-templates in the consumer site: just templates # copies framework + project templates to htmx-templates/ # Restart server (templates loaded at startup): just dev # kills old server, starts fresh # Publish: sync htmx-templates to PV + rollout restart kubectl cp htmx-templates/ :/var/www/htmx-templates/ kubectl rollout restart deployment/ ``` `just templates` runs `assemble-htmx-templates.nu` — it overlays framework defaults with the L2 project's templates. Project templates always win over framework defaults. If `just local-install` was run (binary rebuild), the installed share templates at `~/.local/share/rustelo-htmx-server/htmx-templates/` are also updated. The site's `htmx-templates/` directory takes precedence over the installed share. --- ## 5. Change menus or routes Routes and menus are **compiled into the binary** from `site/config/routes.ncl` at build time. Changing them requires rebuilding the binary. ```sh # 1. Edit in the consumer site (jpl/site, etc.): # site/site/config/routes.ncl — add/change URL paths and menu entries # site/site/config/site.ncl — confirm language list is current # 2. Copy to the L2 source project (website-htmx-rustelo/code): # cp site/site/config/routes.ncl website-htmx-rustelo/code/site/config/routes.ncl # 3. Rebuild the binary from website-htmx-rustelo/code: cd /path/to/website-htmx-rustelo/code just local-install # builds release binary + installs wrapper # 4. Restart the consumer site server: cd /path/to/jpl/site just dev ``` `just local-install` takes ~2 min on first build, incremental afterward (sccache). The new binary at `~/.local/libexec/rustelo-htmx-server` is picked up immediately on next `just dev` — no path changes needed. **Route component name convention:** ``` URL: /my-tool → Component: "MyToolPage" URL: /lian-build → Component: "LianBuildPage" ``` --- ## 6. Update FTL strings (i18n text) FTL files are loaded at **server startup** from `site/i18n/locales/{lang}/`. No rebuild needed; restart applies the changes. ```sh # Edit FTL files: # site/i18n/locales/en/pages/{slug}.ftl # site/i18n/locales/es/pages/{slug}.ftl # site/i18n/locales/en/common.ftl (shared UI strings) # Apply changes (restart server): just dev # kills existing server, starts with fresh FTL # Publish: sync i18n to PV + rollout restart kubectl cp site/i18n/ :/var/www/site/i18n/ kubectl rollout restart deployment/ ``` FTL is fail-open: missing keys render as empty string, never crash. Both languages (`en` and `es`) must be kept in sync — add every key to both. --- ## 7. Add a new language Adding a language requires both FTL files (runtime) and route paths (compile-time). ```sh # Step 1 — Create FTL translations (runtime, no rebuild): mkdir -p site/i18n/locales/{lang}/pages cp site/i18n/locales/en/*.ftl site/i18n/locales/{lang}/ # Translate all strings in the copied files # Step 2 — Register the language (requires rebuild): # In site/config/site.ncl — add to the languages list # In site/config/routes.ncl — add "{lang} = /{path}" to every route # Step 3 — Rebuild binary: cd /path/to/website-htmx-rustelo/code just local-install # Step 4 — Restart: cd /path/to/consumer-site just dev ``` If the new language shares URL paths with English (e.g., language detection via `Accept-Language` header only), steps 2 and 3 may be skipped — only FTL is needed. Verify with `just htmx-smoke` after starting the server. --- ## 8. Change RBAC permissions RBAC is hot-reloaded automatically. The server watches `site/rbac.ncl` for changes and applies them without restart. ```sh # Edit: # site/rbac.ncl — roles, groups, protected paths, anonymous deny paths # No action needed — the running server picks up changes within ~1s. # Confirm in server logs: "rbac hot-reload: N groups, M overrides" ``` If the server is not running, changes take effect on next `just dev`. **For deployment**: sync `site/rbac.ncl` to the PVC — no rollout restart needed. ```sh kubectl cp site/rbac.ncl :/var/www/site/rbac.ncl ``` --- ## Quick reference ```sh just dev # start local server just content [type] [lang] # regenerate content indexes just content-watch [type] [lang] # watch + auto-regenerate just graph # rebuild content_graph.json just templates # rebuild htmx-templates from source just sync # templates + content (no graph) just sync-full # templates + content + graph ``` **When to rebuild the binary** (`just local-install` in `website-htmx-rustelo/code`): - Routes or menu structure changed - New language with distinct URL paths - New page component type added