3.5 KiB
Architecture decisions
Why the site is built the way it is. Read this before questioning one of these
decisions — each records the reasoning, not just the choice. Operational how-to
lives in docs/deployment.md; open work lives in tasks/.
Note on scope. These decisions describe stage 1: a static information site with content as files. The move to editor-based content maintenance (
tasks/001-redaktion-ohne-entwickler.md) deliberately revisits AE-2 and AE-3 — when that lands, update this file.
AE-1: Blazor with static server-side rendering, not WebAssembly
Decision: Blazor Web App with interactivity mode None (pure static SSR). Target framework .NET 10 (LTS).
Rationale: Blazor WASM loads several MB of runtime before the first visible letter, serves search engines and link previews (WhatsApp, Signal, Messenger — the main distribution channel among parents) an empty shell, and provides no value on a pure information site. Static SSR delivers finished HTML, needs no JavaScript, and costs around 60 MB of RAM in the container.
Consequence: Interactivity can be added later per component
(@rendermode InteractiveServer on exactly that one component), without changing the
architecture. Never global.
AE-2: Content as files in the repo, not in a database
Decision: Page content as Markdown with YAML frontmatter, events as structured
YAML (Content/events.yml), documents as PDF under wwwroot.
Rationale: No DB backup, no migration schema, no consistency problems between files and database. Changes are commits and are therefore traceable and reversible. For a site with ~10 subpages and a few events per year, anything else is overhead.
Consequence: Text changes require a commit and a redeploy. With an expected five
changes a year that is acceptable — and the reason the editor expansion
(tasks/001) is treated as a separate stage.
AE-3: Content is baked into the image, not mounted as a volume
Decision: Content/ and wwwroot/ are part of the image.
Rationale: Mounted content from the appdata share could be edited directly on the
NAS, but that is exactly when the repo and the live state drift apart — and the
"backup = git clone" property from AE-2 would be worthless.
Consequence: No quick fix on the live system. Typos are corrected properly via a
commit. Note: the content is read once at startup and cached — there is no
FileSystemWatcher (a change needs a restart / dotnet watch).
AE-4: TLS and certificates exclusively in the Nginx Proxy Manager
Decision: The container speaks only HTTP on port 8080 and has no port mapping to the outside. NPM terminates TLS and is the only path to the container.
Rationale: Matches the pattern already established in the home lab
(cloud.anticarnist.de). Certificate management stays in one place.
Consequence (important): In Program.cs, no UseHttpsRedirection() and
no UseHsts() — otherwise a redirect loop behind the proxy. NPM sets HSTS.
UseForwardedHeaders with emptied KnownNetworks/KnownProxies is correct because
the container has no port mapping and is reachable only through NPM.
Rejected alternatives (for AE-1)
| Alternative | Why not |
|---|---|
| Blazor WASM | Payload, SEO, link previews, no benefit |
| ASP.NET Core MVC/Razor Pages | Works too, but Razor Components are the more modern model |
| Statiq.Web (C# SSG) → nginx | Leanest to operate, but every text change forces a build. Kept as a fallback. |
| Astro/Hugo | More mature SSG ecosystem, but unfamiliar territory |