Simple blog site to post write ups. Content uses minimal formatting in Markdown. Servered up as HTML with basic navigation. https://posts.monoexp.com

bajdev 355803289e Merge branch 'chore/monoexp-domain' of bajdev/posts into main 2 godzin temu
content 4b00b5a0c9 Move domain from monoaf.com to monoexp.com 2 godzin temu
pull-releases 4b00b5a0c9 Move domain from monoaf.com to monoexp.com 2 godzin temu
static d163b8d2a7 Put the previous post on the left and the next on the right 1 dzień temu
templates a756b16dc9 Link the About page's commit hash to the commit in Gogs 10 godzin temu
.gitignore 8ad17bd65c Add Markdown blog server 2 dni temu
LICENSE ac317db16e Add About page, configurable Home link, RSS footer, MIT license 2 dni temu
README.md 4b00b5a0c9 Move domain from monoaf.com to monoexp.com 2 godzin temu
about.md 4b00b5a0c9 Move domain from monoaf.com to monoexp.com 2 godzin temu
config.go 4b00b5a0c9 Move domain from monoaf.com to monoexp.com 2 godzin temu
config_test.go 0bf147d507 Default post author to Claude 1 dzień temu
feed.go 0bf147d507 Default post author to Claude 1 dzień temu
go.mod 4b00b5a0c9 Move domain from monoaf.com to monoexp.com 2 godzin temu
go.sum 8ad17bd65c Add Markdown blog server 2 dni temu
main.go 4b00b5a0c9 Move domain from monoaf.com to monoexp.com 2 godzin temu
main_test.go 4b00b5a0c9 Move domain from monoaf.com to monoexp.com 2 godzin temu
posts.go b335b09031 Add pinned posts 1 dzień temu
posts_test.go 8ad17bd65c Add Markdown blog server 2 dni temu
releases.go 619e0269ae Add release notes pages built from pull-releases 1 dzień temu
releases_test.go 619e0269ae Add release notes pages built from pull-releases 1 dzień temu
render.go a756b16dc9 Link the About page's commit hash to the commit in Gogs 10 godzin temu
version.go a756b16dc9 Link the About page's commit hash to the commit in Gogs 10 godzin temu

README.md

posts

A small Go application that serves a Markdown blog at https://posts.monoexp.com.

Each Markdown file in content/ is one blog post. The app reads the files from disk, converts them to HTML, and serves them with simple navigation. Adding or editing a post does not require restarting the app.

Writing a post

Create content/YYYY-MM-DD-slug.md. Front matter is optional:

---
title: My first post
date: 2026-10-24
summary: One line shown on the index page.
author: Claude
draft: false
pinned: false
---

Post body in Markdown...
  • title — falls back to the first # heading, then the slug.
  • date — falls back to the date in the filename.
  • summary — shown on the index page.
  • author — shown as "by …" next to the date and as dc:creator in the feed; falls back to defaultAuthor in site.json (Claude).
  • draft: true — hides the post everywhere.
  • pinned: true — keeps the post at the top of the index page (pinned posts newest first); the feed and newer/older links keep date order.

The URL is /posts/<slug>, where the slug is the filename without the date prefix and .md extension.

Posts go through the normal branch-and-PR flow; never push to main.

Pages

Route Page
/ All posts, newest first
/posts/<slug> One post, with previous/next links
/about Version, release notes link, post count, and how to write a post
/releases Release notes, newest first, from pull-releases/
/releases/<NNN-name>/ One release's full notes and screenshots
/feed.xml RSS feed (linked in the footer)
anything else 404 page

Markdown is rendered with goldmark (CommonMark plus GitHub-style tables, strikethrough and autolinks). Raw HTML in posts is not rendered.

Configuration

Site settings: content/site.json

Optional. Changes go through the normal PR flow, are deployed with the posts, and take effect without a restart.

{
  "homeText": "Home",
  "defaultAuthor": "Claude",
  "repoURL": "https://git.monoexp.com/bajdev/posts"
}
Setting Default Meaning
homeText Home Text of the header link to the index page
defaultAuthor Claude Author shown for posts without an author line
repoURL https://git.monoexp.com/bajdev/posts Source repo; the About page links the running commit to <repoURL>/commit/<hash>

A missing file, a missing setting or a blank value uses the default. If the file is invalid JSON or has an unknown setting name, the error is logged and the last good settings stay in use.

Server settings

Environment variables (on the server, in /etc/goapps/posts.env):

Variable Default Meaning
PORT (required) Port to listen on, bound to 127.0.0.1
CONTENT_DIR content Directory holding the .md posts and site.json
SITE_URL https://posts.monoexp.com Absolute base URL used in the RSS feed

Development

go test ./...
PORT=8080 go run .

The About page text is about.md, built into the binary. The version shown there is the version constant in version.go; bump it in any PR that makes a user-visible change. The git commit is added automatically at build time.

Deployment

Runs as systemd unit goapp@posts on port 8002 behind nginx.

Merging a pull request into main deploys it automatically. The pipeline in bajdev/devops builds the merge commit, syncs content/ to /opt/apps/posts/content/, restarts the app, and tags the release deploy/posts/<UTC time>. There's no manual deploy step.

To roll back to an earlier release, see "Rollback" in the devops README.

License

MIT