Remove example blog and article entries
Deploy production / deploy (push) Successful in 4s

This commit is contained in:
2026-08-12 08:33:45 -07:00
parent cedcfe584a
commit 427cd17135
3 changed files with 0 additions and 787 deletions
@@ -1,100 +0,0 @@
_model: entry
---
title: Example Article: Anatomy of an Information Card
---
date: 2026-08-11
---
updated: 2026-08-11
---
author: Example Article Author
---
tags: lorem-ipsum, information-card, field-mapping, example
---
kicker: Article
---
summary: A fully populated example article showing how every information-card value maps to a Lektor content field.
---
published_urls:
https://example.com/article
https://publication.example.org/features/anatomy-of-an-article-information-card-demonstration
---
body:
## Information card field map
Every available row in the article information card is intentionally visible
on this example page. The values map to this record's `contents.lr` file as
follows:
`content/articles/example-information-card/contents.lr`
- **Author** is read from the `author` field.
- **Published** is read from the `date` field.
- **Updated** is read from the optional `updated` field.
- **Reading time** is calculated automatically from the words in the `body`
field, using approximately 200 words per minute.
- **Topics** are read from the controlled checkbox `tags` field.
- **Published online** loops over every item in the multiline `published_urls`
field.
The first publication URL stays below 40 characters and is displayed in full.
The second is deliberately longer than 40 characters, so its visible text uses
a middle ellipsis while its link destination, tooltip, and accessible label
retain the complete URL.
## Lorem ipsum demonstration
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere,
libero non posuere luctus, neque tellus cursus sapien, vitae tincidunt erat
metus et velit. Sed dignissim sem vel augue fermentum, vitae tristique lorem
facilisis. Praesent commodo, mauris id faucibus malesuada, augue justo
sollicitudin nibh, vitae efficitur magna arcu sed neque. Nulla facilisi. Donec
tempor turpis sed felis interdum, quis consequat nibh consequat.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
cubilia curae; Etiam id lectus vel ipsum placerat pellentesque. Morbi vulputate
semper dolor, in facilisis justo volutpat sed. Suspendisse potenti. Fusce
ultricies ipsum at arcu aliquet, a feugiat augue suscipit. Nam interdum
fermentum enim, et commodo sapien pharetra sit amet. Curabitur vitae urna vitae
mauris volutpat consequat non non augue.
Aliquam erat volutpat. Proin vitae metus eget nunc porttitor elementum. Mauris
euismod, sem sed tincidunt tincidunt, sapien velit consectetur neque, at
viverra nibh eros nec urna. Duis malesuada sapien in velit feugiat, sed
ullamcorper massa tincidunt. Cras condimentum risus at neque vestibulum, quis
tempor justo tristique. Pellentesque habitant morbi tristique senectus et netus
et malesuada fames ac turpis egestas.
Donec eu feugiat neque. Quisque ac velit vitae odio volutpat vulputate. Nunc
faucibus eros non dolor tempor, nec posuere lacus tristique. Sed euismod
convallis nulla, quis feugiat dolor vulputate sit amet. Aenean commodo feugiat
ipsum, vel dictum sapien ullamcorper quis. Vivamus id enim nec nibh eleifend
convallis. In hac habitasse platea dictumst. Maecenas cursus orci eget dui
aliquet, vitae feugiat elit rhoncus.
Phasellus dignissim, metus ac tristique convallis, purus massa pulvinar enim,
eu facilisis justo sem sit amet nibh. Nullam gravida interdum quam, a luctus
turpis finibus sed. Sed at magna vel lectus faucibus malesuada. Integer
facilisis quam non purus volutpat, quis tristique nisl blandit. Ut pellentesque
ligula vitae purus porttitor, vitae scelerisque justo feugiat. Duis luctus
metus quis orci egestas, id pellentesque mi faucibus.
Morbi sit amet nibh et justo sagittis viverra. Nulla vitae odio ac tortor
tempor pretium. Curabitur dignissim massa at justo eleifend, vel feugiat neque
facilisis. Praesent sed tellus quis augue vulputate malesuada. Sed tincidunt
nisi in risus dignissim, a hendrerit ipsum volutpat. Integer faucibus metus et
turpis sodales, sed convallis lectus feugiat. Vivamus finibus, tortor id
efficitur feugiat, odio mauris imperdiet justo, quis pulvinar erat sem at arcu.
Etiam consequat ligula sed sem faucibus, eu feugiat leo posuere. Pellentesque
congue mauris sit amet sapien vestibulum, quis vulputate nulla malesuada.
Suspendisse vitae ipsum ut sapien consequat tempus. Aenean et lectus in justo
scelerisque feugiat. Cras sed posuere ex. Nam non egestas risus. Vestibulum
mollis mauris eget nisi efficitur, vitae sollicitudin tortor imperdiet. Donec
at consequat velit, non cursus risus.
Sed vulputate justo at elit gravida, a tristique mauris blandit. In hac
habitasse platea dictumst. Mauris at erat luctus, volutpat risus vitae,
fermentum ipsum. Proin malesuada nibh et orci tristique, eget efficitur nibh
interdum. Quisque feugiat sem at purus pretium, id sodales nunc ullamcorper.
Integer accumsan urna sit amet mi placerat, vitae suscipit tortor lacinia.
@@ -1,668 +0,0 @@
_model: entry
---
title: Example Blog Entry: Labyricorn Deployment Runbook
---
date: 2026-08-11
---
author: Example Blog Author
---
tags: lektor, deployment, operations, example
---
kicker: Blog
---
summary: A complete example blog record whose body mirrors the repository runbook and documents how its information card is populated.
---
body:
## Information card field map
This example deliberately exposes how the visible post-information card maps
back to this record's `contents.lr` file:
`content/blog/example-deployment-runbook/contents.lr`
- **Author** is read from the `author` field.
- **Published** is read from the `date` field.
- **Tags** are read from the controlled checkbox `tags` field.
The title, summary, and article body are supplied by `title`, `summary`, and
`body`, respectively, but those values appear outside the information card.
## Labyricorn website and deployment runbook
This repository is the canonical source for `www.labyricorn.com`. It contains
the editable Lektor project—not generated production files—and is also the
primary operations guide for building, deploying, verifying, rolling back, and
troubleshooting the site.
## Quick reference
| Item | Value |
| --- | --- |
| Production URL | `https://www.labyricorn.com` |
| Canonical Git repository | `https://git.labyricorn.com/Labyricorn/labyricorn-site.git` |
| Production branch | `main` |
| Production host | `ubuntu2-47` (`10.138.2.47`) |
| Repository checkout | `/srv/labyricorn/repo` |
| Active document root | `/srv/labyricorn/current` |
| Release storage | `/srv/labyricorn/releases` |
| Deployment command | `sudo deploy-labyricorn` |
| Rollback listing | `sudo rollback-labyricorn` |
| Rollback command | `sudo rollback-labyricorn RELEASE_NAME` |
| Deployment log | `/srv/labyricorn/logs/deploy.log` |
| nginx site config | `/etc/nginx/sites-available/labyricorn` |
| Lektor executable | `/srv/labyricorn/.local/bin/lektor` |
| Deployment account | `labyricorn-deploy` |
## Architecture
```text
Gitea: Labyricorn/labyricorn-site (main)
|
| authenticated Git fetch
v
/srv/labyricorn/repo
|
| Lektor static build
v
/srv/labyricorn/releases/<UTC timestamp>-<commit>
|
| validated, atomic symlink switch
v
/srv/labyricorn/current
|
| read-only static files
v
nginx :80
|
| Cloudflare proxy and public TLS
v
https://www.labyricorn.com
```
Gitea is the source of truth. The production checkout is disposable state and
must not contain unpublished edits during deployment. Lektor's development
server is not part of production serving; nginx serves generated files only.
## Repository contents
```text
Labyricorn.lektorproject Lektor project definition
content/ Editable site content (`contents.lr` files)
content/tags/ Controlled tag vocabulary and dedicated tag routes
models/ Lektor content models
templates/ Jinja templates
assets/static/ CSS, filtering JavaScript, favicon, and static assets
.gitignore Excludes generated and local files
README.md This runbook
```
Generated output such as `build/`, `dist/`, and `.lektor/` is intentionally
ignored and must not be committed.
## Routine content workflow
Edit in a normal Git checkout, review the changes, and push them to `main`.
A successful push triggers the repository's Gitea Action, which builds and
atomically activates that exact revision on the production host.
```bash
git switch main
git pull --ff-only origin main
# Edit content, templates, models, or assets.
lektor build --output-path build
git status
git add <reviewed-files>
git commit -m "Describe the site change"
git push origin main
```
Watch the run under **Actions** in Gitea. Manual deployment remains available
for recovery or controlled operations:
```bash
sudo deploy-labyricorn
```
Do not edit generated files under `/srv/labyricorn/current` or
`/srv/labyricorn/releases`. They will be replaced by deployment and are not
source-controlled.
## Tags and filtering workflow
`content/tags/` is the single source of truth for the controlled tag
vocabulary. Each child directory is a normal Lektor record whose directory name
is the canonical lowercase, hyphenated slug used in entry data and URLs:
```text
content/tags/deployment/contents.lr -> /tags/deployment/
```
The tag record supplies the editor label and public description:
_model: tag
---
title: Deployment
---
summary: Build, release, rollout, verification, and rollback workflows.
The entry model reads its checkbox choices directly from these records. Blog
entries and articles store the selected slugs as a comma-separated `tags`
value:
```text
tags: lektor, deployment, operations
```
To add a tag, create its `content/tags/<slug>/contents.lr` record, run a build,
and then select it on the relevant entries. Do not add a slug directly to an
entry before its tag record exists. To rename or retire a tag, update every
referencing entry deliberately and verify that no old slug remains before
renaming or removing its record.
`/tags/` lists every approved tag and counts its usage across projects, project
devlog entries, blog entries, and articles. Dedicated tag URLs combine matching
records from all four content types. Tag links are normal links and work without
JavaScript. On the Projects, Blog, and Articles listing pages,
`assets/static/tags.js` progressively adds instant filtering and keeps the
selected filter in the `tag` query parameter.
After taxonomy or filtering changes:
1. Run `python scripts/build_with_projects.py --output-path build`.
2. Verify `/tags/`, at least one dedicated tag URL, the three top-level section
listings, and a project devlog index.
3. Confirm tag counts cover projects, devlog entries, blog entries, and articles
and that every stored entry slug has a matching tag record.
4. Test filtering with JavaScript enabled and confirm tag links remain usable
without JavaScript.
5. Check keyboard operation plus desktop and narrow layouts.
6. If filtering JavaScript or tag styles changed, increment the corresponding
cache-busting version in `templates/base.html`.
## Local development
Install Lektor in an isolated Python environment. For example:
```bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install lektor
```
Run the local editor and preview server:
```bash
lektor server
```
Run a production-style build:
```bash
lektor build --output-path build
test -f build/index.html
```
The project currently has no third-party Lektor packages or plugins.
## Production filesystem and permissions
```text
/srv/labyricorn/
├── repo/ Git working copy (not web-accessible)
├── releases/ Successful immutable builds
├── current -> releases/<release>
├── logs/
│ ├── deploy.log
│ └── deploy.lock
├── .local/bin/lektor pipx-installed Lektor
├── .netrc Git HTTPS credential (0600; secret)
├── .ssh/ Reserved deployment SSH material
└── README-DEPLOYMENT.md Supplemental host-local notes
```
The `labyricorn-deploy` account owns the checkout, releases, logs, Lektor
environment, and Git credential. nginx's `www-data` account can traverse the
base path and read active generated files, but cannot read the source checkout
or credential. nginx cannot modify either source or releases.
## Deployment behavior
`/usr/local/bin/deploy-labyricorn` performs the following sequence:
1. Changes from root to the unprivileged `labyricorn-deploy` account.
2. Acquires an exclusive `flock` lock to prevent concurrent deployments.
3. Refuses to proceed if the working tree contains any tracked or untracked
changes.
4. Fetches and prunes `origin`.
5. Verifies that `origin/main` exists.
6. Switches to local `main` and resets it exactly to `origin/main`.
7. Locates exactly one `.lektorproject` file instead of assuming its name.
8. Creates a unique release named with UTC time and the 12-character commit ID.
9. Runs Lektor with an explicit output directory.
10. Requires the build to succeed and contain `index.html`.
11. Writes the full commit ID to `.labyricorn-commit` in the release.
12. Atomically replaces `/srv/labyricorn/current` with a relative symlink to
the new release.
13. Keeps the active release plus recent prior releases, targeting five total.
14. Logs start, failure, success, release name, and commit.
Run it with:
```bash
sudo deploy-labyricorn
```
A failed build is removed before activation. The current symlink is not changed,
so the previously working site remains online.
### Deployment preflight
```bash
sudo -u labyricorn-deploy env HOME=/srv/labyricorn \
git -C /srv/labyricorn/repo status --short --branch
sudo -u labyricorn-deploy env HOME=/srv/labyricorn \
git -C /srv/labyricorn/repo fetch origin
```
The status must be clean. Do not bypass a dirty-tree refusal. Determine whether
the files are legitimate unpublished admin edits, then commit and push them or
remove them deliberately before deployment.
## Verify a deployment
```bash
readlink -f /srv/labyricorn/current
cat /srv/labyricorn/current/.labyricorn-commit
sudo -u labyricorn-deploy env HOME=/srv/labyricorn \
git -C /srv/labyricorn/repo rev-parse HEAD
sudo -u labyricorn-deploy env HOME=/srv/labyricorn \
git -C /srv/labyricorn/repo ls-remote origin refs/heads/main
nginx -t
systemctl is-active nginx
curl -I -H 'Host: www.labyricorn.com' \
-H 'X-Forwarded-Proto: https' http://127.0.0.1/
curl -I https://www.labyricorn.com/
```
The commit in the active release, local `HEAD`, and remote `main` should match.
The local nginx request with `X-Forwarded-Proto: https` should return `200`.
Public HTTP should redirect to HTTPS, and public HTTPS should return `200`.
## Rollback
List releases and identify the current one:
```bash
sudo rollback-labyricorn
```
Switch atomically to a previous successful release:
```bash
sudo rollback-labyricorn 20260812T024742Z-92475d2ec759
```
Use a release name printed by the listing command. Rollback does not build,
fetch, or alter Git. It only changes the `current` symlink and records the event
in the deployment log.
Verify afterward:
```bash
readlink -f /srv/labyricorn/current
cat /srv/labyricorn/current/.labyricorn-commit
curl -I -H 'Host: www.labyricorn.com' \
-H 'X-Forwarded-Proto: https' http://127.0.0.1/
```
A normal deployment after rollback rebuilds and reactivates the latest remote
`main` commit.
## nginx and URL handling
The site configuration is:
```text
/etc/nginx/sites-available/labyricorn
/etc/nginx/sites-enabled/labyricorn -> ../sites-available/labyricorn
```
nginx serves `/srv/labyricorn/current` and supports:
- `index.html` at directory-style URLs;
- direct static assets;
- optional `.html` fallback;
- generated `/404/index.html` with an HTTP `404` response;
- disabled directory browsing;
- rejection of hidden paths and common executable-script extensions;
- dedicated access and error logs.
Always validate before reloading nginx:
```bash
nginx -t
systemctl reload nginx
```
nginx logs:
```text
/var/log/nginx/labyricorn-access.log
/var/log/nginx/labyricorn-error.log
```
## HTTPS, Cloudflare, and DNS
Current production HTTPS terminates at Cloudflare. The origin nginx process
currently listens on port `80`, not `443`. For `www.labyricorn.com`, nginx uses
Cloudflare's `X-Forwarded-Proto` header to distinguish public HTTP from HTTPS:
- public HTTP redirects to `https://www.labyricorn.com`;
- Cloudflare-originated HTTPS requests are served without a redirect loop;
- `labyricorn.com` is configured at nginx to redirect to the canonical HTTPS
`www` hostname.
The apex `labyricorn.com` did not resolve during initial setup. Its redirect will
not work publicly until DNS/Cloudflare has an apex record routed to this origin.
Certbot and the nginx integration are installed, and `certbot.timer` is enabled,
but no origin certificate was issued during setup. Do not run Certbot blindly
behind Cloudflare. First decide whether the desired origin mode is a Cloudflare
Origin CA certificate, Let's Encrypt with a compatible challenge, or direct DNS
without the proxy. After changing TLS, test both origin and public behavior for
redirect loops.
Useful checks:
```bash
getent ahosts www.labyricorn.com
getent ahosts labyricorn.com
curl -I http://www.labyricorn.com/
curl -I https://www.labyricorn.com/
systemctl status certbot.timer
certbot certificates
```
## Firewall and exposed services
UFW is active with default incoming denial. Allowed services are:
- OpenSSH: TCP 22
- nginx HTTP/HTTPS profile: TCP 80 and 443
Inspect current state:
```bash
ufw status verbose
ss -lntup
```
Lektor's development port must never be opened in UFW or bound to `0.0.0.0`.
## Optional server-side Lektor admin
An optional unit exists at:
```text
/etc/systemd/system/lektor-admin.service
```
Current state: **disabled and stopped**. When started, it runs as
`labyricorn-deploy` and binds only to `127.0.0.1:5000`. It is not proxied by
nginx and production remains independent of it.
Start it temporarily:
```bash
systemctl start lektor-admin
systemctl status lektor-admin
ss -lntp | grep ':5000'
```
Access it securely through an SSH tunnel from an administrator workstation:
```bash
ssh -L 5000:127.0.0.1:5000 [email protected]
```
Then browse to `http://127.0.0.1:5000` on that workstation.
Stop it when finished:
```bash
systemctl stop lektor-admin
```
### Committing admin edits made on the server
Lektor can edit files, but it does not automatically create Git commits or push
them. The deployment account intentionally has authenticated Git write access so
a controlled admin workflow can publish server-side edits.
Review everything before committing:
```bash
sudo -u labyricorn-deploy env HOME=/srv/labyricorn bash
cd /srv/labyricorn/repo
git status
git diff
git add <reviewed-files>
git commit -m "Describe the admin edit"
git push origin main
exit
```
Then run `sudo deploy-labyricorn`. The deployment script will refuse to run
while uncommitted admin edits remain. This is deliberate protection against
silently discarding edits when it resets to `origin/main`.
Before enabling persistent or public admin access, add authentication and make a
separate security decision. Do not publish port 5000 directly and do not proxy
an unauthenticated editor through nginx.
## Git authentication and secrets
The private Gitea repository is accessed over HTTPS because the Gitea SSH port
was not reachable through `git.labyricorn.com` during setup.
Credential purpose and location:
| Purpose | Location | Permissions |
| --- | --- | --- |
| Gitea HTTPS fetch/push | `/srv/labyricorn/.netrc` | `0600`, owned by `labyricorn-deploy` |
| Reserved SSH identity | `/srv/labyricorn/.ssh/id_ed25519` | `0600`, owned by `labyricorn-deploy` |
Never place token contents, passwords, or private keys in this README, Git URLs,
shell scripts, logs, commits, issue trackers, or command history.
Check credential-file metadata without printing its contents:
```bash
stat -c '%A %U:%G %n' /srv/labyricorn/.netrc
```
If the Gitea token is rotated, replace `.netrc` as `labyricorn-deploy`, preserve
mode `0600`, and verify both fetch and push access before revoking the old token.
Avoid printing either credential during rotation.
## Logs and diagnostics
```bash
# Deployment and rollback history
tail -n 100 /srv/labyricorn/logs/deploy.log
# nginx service and request errors
journalctl -u nginx --since today
tail -n 100 /var/log/nginx/labyricorn-error.log
# Optional admin service
journalctl -u lektor-admin --since today
# Repository state
sudo -u labyricorn-deploy env HOME=/srv/labyricorn \
git -C /srv/labyricorn/repo status
# Active release
readlink -f /srv/labyricorn/current
cat /srv/labyricorn/current/.labyricorn-commit
# Services and sockets
systemctl status nginx
systemctl status certbot.timer
ss -lntup
```
## Troubleshooting
### Deployment says the repository is dirty
Inspect as the deployment account:
```bash
sudo -u labyricorn-deploy env HOME=/srv/labyricorn \
git -C /srv/labyricorn/repo status --short
```
If these are intended admin edits, review, commit, and push them. Otherwise,
identify their origin before removing anything. Never use a destructive reset
until you are certain no work needs to be preserved.
### Git fetch or push fails
Check DNS, HTTPS, the remote URL, and credential permissions without displaying
the secret:
```bash
getent ahosts git.labyricorn.com
curl -I https://git.labyricorn.com/
sudo -u labyricorn-deploy env HOME=/srv/labyricorn \
git -C /srv/labyricorn/repo remote -v
stat -c '%A %U:%G %n' /srv/labyricorn/.netrc
```
An authentication failure usually means the token was revoked, expired, or
lacks repository access.
### Lektor build fails
Run a disposable diagnostic build; do not build into `current`:
```bash
test_dir=$(mktemp -d /srv/labyricorn/releases/.diagnostic.XXXXXX)
chown labyricorn-deploy:labyricorn-deploy "$test_dir"
sudo -u labyricorn-deploy env HOME=/srv/labyricorn \
/srv/labyricorn/.local/bin/lektor \
--project /srv/labyricorn/repo/Labyricorn.lektorproject \
build --output-path "$test_dir"
```
Inspect the error and source. Remove the diagnostic directory only after
confirming its exact path and that it is not referenced by `current`.
### nginx returns an error
```bash
nginx -t
systemctl status nginx
readlink -f /srv/labyricorn/current
test -f /srv/labyricorn/current/index.html
tail -n 100 /var/log/nginx/labyricorn-error.log
```
Do not point nginx at the Git checkout or start Lektor as a production server.
### Public HTTPS loops or behaves differently from local HTTP
Check Cloudflare proxy mode and the forwarded protocol behavior:
```bash
curl -I -H 'Host: www.labyricorn.com' http://127.0.0.1/
curl -I -H 'Host: www.labyricorn.com' \
-H 'X-Forwarded-Proto: https' http://127.0.0.1/
curl -I http://www.labyricorn.com/
curl -I https://www.labyricorn.com/
```
The first origin request should redirect; the simulated Cloudflare HTTPS request
should return the site. A different result indicates a proxy/header or nginx
configuration problem.
## Automatic deployment
Pushes to `main` automatically deploy through Gitea Actions. The workflow is
versioned with the site at `.gitea/workflows/deploy.yml` and has one job:
1. Run on the repository-scoped runner labelled `labyricorn-deploy`.
2. Execute the existing root-owned `deploy-labyricorn` command.
3. Confirm `/srv/labyricorn/current/.labyricorn-commit` equals the pushed commit.
4. Request the site through nginx on the local origin and fail if it is unhealthy.
The action intentionally does not check out the repository into its own
workspace. The deployment script fetches `origin/main` using the dedicated
`labyricorn-deploy` account, validates a clean checkout, builds a new release,
and atomically changes the `current` symlink. Concurrent action runs share the
`labyricorn-production` concurrency group, and the deployment script also uses
a filesystem lock.
### Runner installation
The production host runs official Gitea Runner 3.0.0 directly on Linux; Docker
is not installed or required for this workflow.
| Item | Value |
|---|---|
| systemd unit | `gitea-runner.service` |
| service account | `gitea-runner` |
| runner name | `labyricorn-production` |
| registration scope | `Labyricorn/labyricorn-site` repository |
| execution label | `labyricorn-deploy:host` |
| runner binary | `/usr/local/bin/gitea-runner` |
| runner configuration | `/etc/gitea-runner/config.yaml` |
| registration state | `/var/lib/gitea-runner/.runner` |
| job workspace | `/var/lib/gitea-runner/work` |
| sudo policy | `/etc/sudoers.d/gitea-runner-labyricorn` |
The runner account has no general administrative access. Its sudo policy allows
only this exact command without a password:
```text
/usr/local/bin/deploy-labyricorn
```
Useful checks:
```bash
systemctl status gitea-runner
journalctl -u gitea-runner -n 100 --no-pager
sudo -l -U gitea-runner
```
If the runner must be replaced, delete or disable it in **Repository Settings →
Actions → Runners**, generate a fresh repository registration token, and
register the replacement. Registration tokens and `.runner` contents are
credentials: never commit or print them. A push can still be deployed manually
with `sudo deploy-labyricorn` while the runner is unavailable.
## Change-control checklist
Before changing deployment, nginx, TLS, firewall, credentials, or admin access:
1. Inspect the live state and preserve unrelated configuration.
2. Back up the specific file being changed.
3. Validate syntax before reload or restart.
4. Keep SSH reachable before firewall changes.
5. Verify local origin behavior and public Cloudflare behavior separately.
6. Record operational changes in this README.
7. Commit and push source documentation to `main`.
This README describes the current verified installation on `ubuntu2-47`. Update
it whenever the operational architecture changes.
-19
View File
@@ -1,19 +0,0 @@
_model: entry
---
title: Hello from Labyricorn
---
date: 2026-08-11
---
author: Labyricorn
---
tags: lektor, git, publishing
---
kicker: Blog
---
summary: The first entry from the new Git-backed Lektor site.
---
body:
The new Labyricorn site starts here: an intentionally small publishing system
with content that is easy to edit, review, build, deploy, and roll back.
More project notes, articles, and experiments will follow.