demo article in English: stack overview and redesigned architecture diagrams

This commit is contained in:
2026-09-11 13:58:49 +02:00
parent 43e2bb4061
commit 9ca89f3e40
13 changed files with 573 additions and 524 deletions
+191
View File
@@ -0,0 +1,191 @@
---
title: "How this portal works: static pages, a visual editor, git as the database"
description: "The architecture behind this demo: a visual editor in the browser, content as Markdown in git, and a public site that is nothing but static files. With an honest comparison against database-backed CMS platforms."
date: 2026-09-11
category: demo
author: Cyfronet
cover: /uploads/diagrams/cover-git-cms.svg
draft: false
---
This portal is a set of **static HTML files** generated from Markdown kept in a
git repository. Editors get a **visual editor in the browser**, and still
**nothing on the public side runs an application or a database**: between the
reader and the content there is only a file server. The one service that does
run, the git server, stands behind the editor and off the reader's path, and we
come back to that honestly below. This article was written and published
through exactly the path it describes.
## The stack in three parts
![The three parts of the stack: Sveltia CMS as the editor, git and Gitea as the database, Astro as the build-time generator, with only nginx and static files involved when a reader opens a page](/uploads/diagrams/stack-overview.svg)
**Sveltia CMS is the editor.** It is one JavaScript file served as a static page
under `/admin/`. It signs the editor in to Gitea and writes Markdown files and
images straight into the repository through the git API. There is no CMS server
and no database behind it, and every save is a commit.
**Git, served by Gitea, is the database.** An article is a Markdown file with a
defined set of fields, and uploaded images sit next to it. History, diffs and a
one-command rollback come from git itself rather than from a paid tier of a
product.
**Astro is a generator, not a server.** Its build turns those Markdown files
into a directory of plain HTML, CSS and images, so deployment is copying that
directory onto a file server. Nothing from Astro runs in production. Pages ship
without JavaScript by default, while components, React among them, are rendered
to HTML during the build, so an interactive dashboard can later be added as an
island on the same site without changing the stack. The build also checks every
article against the defined fields, which means a malformed article stops the
build instead of reaching the public site.
On the reader's side that leaves one moving part: nginx handing over files.
## From a save to a published page
![Five steps of publishing: save in the editor, commit in the repository, change detected, site build, published](/uploads/diagrams/publish-flow.svg)
Publishing is automatic. A save in the editor shows up on the site moments
later, with no manual deployment step in between. Every change is a commit, so
**a full content history and a one-command rollback come for free**.
## How it runs on the machine
![Architecture on the demo machine: the editor writes through Sveltia into the Gitea content repository, a systemd timer runs a one-shot Astro build container, the result lands in the releases volume and nginx serves readers static files only](/uploads/diagrams/architecture-machine.svg)
Two repositories, by design. The portal **code** is developed by the team, the
editorial **content** is written by the CMS. Every build takes the newest commit
from both, overlays content onto code and renders the site, so a push to either
repository republishes. Editors never touch the code repository, and
development never mixes with the editorial history.
Everything runs rootless in podman on a single machine: a Gitea container, an
nginx container, and a one-shot build container started by a systemd timer. A
build that fails changes nothing, because the previous release keeps serving.
For comparison, the same machine running a classic CMS. The diagram keeps the
same skeleton on purpose, so the difference is visible at a glance: every
element is up around the clock, the editor and the reader talk to the very same
application, and its login panel faces the internet.
![Architecture of a database-backed CMS: an application with a public admin panel and on-demand rendering runs non stop, next to a database that needs backups and a separate media volume](/uploads/diagrams/architecture-cms-database.svg)
## Where things are
| What | Where |
|---|---|
| Portal | this site |
| Content editor | `/admin/`, the "Content editor" link in the footer |
| Content repository | `ctao/content` on the demo machine's Gitea, written by the CMS |
| Code repository | `ctao/portal`, maintained by the team |
| Deployment documentation | `deploy/README.md` in the code repository |
## Why a static site
![Attack surface compared: a database-backed CMS exposes an application, a login panel, a database and plugins, while this solution exposes static HTML files only and keeps editing behind a git sign-in](/uploads/diagrams/attack-surface.svg)
- **Security.** What faces the internet is a file server and a directory of
files. There is no admin panel to attack, no database to exfiltrate and no
application runtime carrying its own vulnerabilities.
- **Maintenance.** There is no application that has to be monitored and upgraded
under patch pressure. What remains is a git service, which the team runs
anyway, and a short build script.
- **Speed.** Static HTML is as fast as a page gets, which search engines reward
as well.
- **History.** Versioning is a property of git, not a feature tier of a product.
## Two classes of solution
The dividing line is often misread. The question is not whether content sits in
a database or in files. It is **whether a running application stands between the
reader and the content**.
| | Static + git CMS *(this demo)* | CMS that serves the site itself |
|---|---|---|
| Examples | Sveltia, Decap (+ Astro / Hugo / Eleventy) | Strapi, WordPress, Drupal, Grav, TinaCMS |
| Content lives in | Markdown files in git | usually a database; Grav in files, Tina in git |
| In production you run | a file server | an application, usually with a database, non stop |
| Maintenance | a build script plus a git service | patches, upgrades, database backups |
| Change history | git, built in | product-dependent, sometimes paid |
Two clarifications, so the table is not read too simply. **Grav** keeps content
in files with no database at all, yet a PHP application still serves every page,
which puts it in the right-hand column for attack surface and maintenance.
**TinaCMS** keeps content as Markdown in git, but its editor needs a backend
with a database in order to work, so it inherits the costs of both worlds.
Limits of the individual products, verified during our research:
- **Strapi**: content version history does not exist in the free self-hosted
edition (Growth and Enterprise plans only). It needs Node and PostgreSQL
running at all times, plus regular upgrades.
- **WordPress**: the largest ecosystem, and also the longest vulnerability
history, mostly through plugins; a public login panel and PHP to patch
continuously.
- **Grav** (PHP, flat file, the "light" alternative): serious remote code
execution issues in recent years, and still a PHP server facing the internet.
- **TinaCMS**: it edits Markdown, but it requires a backend. The Tina cloud is
free for up to two users, and self-hosting means your own Node server, a
database and authentication, which is an application to maintain again.
- **Open-core risk**: features can move into paid plans over time, as above.
When the content and its history live in git, there is no feature a vendor
can move behind a paywall.
## Trade-offs over six years
The portal is meant to be maintained for at least six years, so the balance has
to be drawn over two horizons. What follows judges the two approaches as classes
of solution, not individual products.
**Short term: launch and the first months**
| | Static + git | CMS with a database |
|---|---|---|
| Strengths | few moving parts from day one; a visual editor ready to use; history and rollback immediately, in git | a richer panel out of the box: roles and permissions, editorial workflow, relations between content types, more ready-made integrations |
| Weaknesses | simple permissions, inherited from git, without editorial roles; browsing history happens in the git interface rather than in the editor | more components to stand up, connect and secure from day one: application, database, backups |
**Over six years**
| | Static + git | CMS with a database |
|---|---|---|
| Strengths | no application on the reader's path, so no patch pressure there; what stays is a git service, ideally the one the team already runs; content in an open format survives any change of tooling | if application features are eventually needed (accounts, personalisation, strongly structured content), the platform already has them |
| Weaknesses | at very large scale (thousands of pages) build times grow and need attention; advanced editorial features depend on the pace of open-source tools | several major version migrations in six years, with breaking changes; the database needs backups and restore drills; a permanent attack surface; features can move into paid tiers |
## "Isn't Gitea just another CMS?"
A fair question. Gitea also has a login, a database and security updates, and it
has to be reachable by editors. Does the difference come down to maintaining
Gitea instead of a CMS?
Partly yes, and we do not claim that maintenance is zero. The difference rests
on three things:
- **Gitea is off the reader's path.** It can be down, mid-upgrade, or even
compromised while the portal keeps serving safe files. The worst case of a
break-in is content vandalism: visible, fully recorded, and undone with a
single revert. In a CMS that serves the site, the same incident means a
compromised public site.
- **The team already runs Gitea.** The content repository can live on the
instance the team maintains anyway, which brings the marginal cost close to
zero and lets one AAI integration handle sign-in.
- **Class of software.** Gitea is a single Go program with SQLite: an upgrade is
swapping an image and restarting, with no plugin ecosystem and no server-side
npm dependency tree. CMS platforms are application frameworks with regular
major-version migrations.
## No lock-in
Every element can be replaced on its own, because the content is plain Markdown:
- **Sveltia to Decap**: the same configuration format, so switching is one
script tag.
- **Astro to Hugo or Eleventy**: the Markdown stays untouched, only templates
change.
- **Gitea to any git hosting**: GitHub, GitLab, or an internal service.
- Even dropping the whole approach is a trivial export, because the content has
been sitting in an open format in our own repository from the start.
The conclusion we draw from this demo: start with the simplest solution that
meets the requirements, which means a visual editor for the editors and a public
site that needs no care. Add complexity only when a need appears that this
approach cannot serve.