Skip to content
Velta GetDownload
Menu

Home

Project

Velta CMS

· 4 min read

The site edits itself through four JSON operations against a git host. Any host that can answer them — including through hooks — can run this CMS.

What it is

Velta CMS is a single-page tool that edits this site straight in its repository. Every page is a complete HTML document that renders without JavaScript; shared chrome lives in _partials/; the CMS assembles pages from those parts and commits them. There is no server, no database, and no build step of its own — the git host is the backend, and git history is the undo.

The GitHub-specific code is one small module in the site repository: admin/backend-github.js. Everything else in the CMS talks to a four-operation contract, described below. Point a different module at a different host and the CMS does not notice.

The contract

Four operations, JSON over HTTPS, all content base64 in both directions. A reference implementation is admin/backend-github.js in the site source.

list(dir)   →  GET  {base}/{dir}?ref={branch}
            ←  [ { "path": "news/index.html", "type": "file",
                   "size": 1234, "sha": "e69de…" } ]

get(path)   →  GET  {base}/{path}?ref={branch}
            ←  { "path": "news/index.html", "sha": "e69de…",
                 "content": "PCFET0NUWVBF…" }

put(path)   →  PUT  {base}/{path}
               { "message": "Update news/index.html",
                 "content": "PCFET0NUWVBF…",
                 "branch": "website", "sha": "e69de…" }
            ←  { "content": { "sha": "4b825d…" } }

del(path)   →  DELETE  {base}/{path}
               { "message": "Delete assets/photo.webp",
                 "sha": "e69de…", "branch": "website" }

Three rules give the contract its shape:

Other git hosts

Implement the four operations against the host's files API and swap the module:

Host Files API Notes
GitHub /repos/{owner}/{repo}/contents The reference implementation; everything above maps 1:1.
Gitea, Forgejo, Codeberg /api/v1/repos/{owner}/{repo}/contents Same request and response shapes as GitHub — mostly a base-URL swap.
GitLab /api/v4/projects/{id}/repository/files Different shape: per-file endpoints and a commits API for writes. Needs a small mapper that composes the four operations from it.

A host with no files API at all can still qualify through hooks: if the platform can receive a webhook that carries (or triggers a fetch of) file content and can commit it — a CI job, a bot, a small receiver service — then that receiver implements put and del, and read-only hosts can serve list and get from a mirror. The contract only needs the four answers; it does not care what stands behind them.

Publishing is the host's job

The CMS only commits. Turning a commit into a live site belongs to the host's push hook: GitHub Pages rebuilds this site on every push to the website branch; on another host you point the equivalent webhook or CI job at your static builder. That separation is deliberate — the editing side never knows how publishing happens, and the publishing side never knows an editor exists.

The CMS itself is a few hundred lines of vanilla JavaScript in admin/admin.js, on the website branch of the repository alongside this page.