The blog CMS where your content IS a git repository.
Self-hosted, single-binary, markdown-first. Every save is a commit — history, rollback, and diff come free.
Status: active development. postify runs www.fakih.net in production and is actively developed. Core authoring flows (web + mobile) work today, but APIs and UX are still evolving — expect occasional breaking changes before 1.0. Issues, ideas, and PRs are very welcome.
Most CMSes hide your content in a database. postify stores posts and pages as plain .md files in a git repository that acts as the storage and versioning engine:
- No lock-in. Your writing is markdown in a repo you own. Point the config at any git repo and it just works.
- Git history for free. Every save is a commit.
git logis your edit history — roll back any change, diff any revision. - One deployable. A single Go binary embeds the admin SPA and serves the public site, the API, and
/admin. No separate frontend server, no container layer, no database server to run. - Fast public reads. Rendered content is cached in SQLite; the public site never hits git per-request.
- Blog-first, not dashboard-first. Public pages are server-rendered by Go — readers get fast HTML with per-page OG/meta tags and zero client JS required to read the site.
- Git as the content store — markdown posts (
posts/), pages, drafts, and publish/unpublish viagit mv - Server-rendered public site —
html/template, per-page SEO/OG tags, generated/sitemap.xmland/robots.txt - WYSIWYG markdown admin — Svelte SPA with Milkdown editor, embedded via
go:embed - Mobile authoring app — Flutter (iOS + Android) with one-step device pairing via QR code; supports multiple postify servers
- Auth — JWT sessions, email + password, and Google OAuth; single-admin by design
- Image storage with backup — primary GitHub assets repo (CDN-served) + Cloudflare R2 dual-write, with background retry
- SEO ready — canonical URLs, OG tags, sitemap, robots,
noindexpreview links for drafts - Preview links — stateless, HMAC-signed short-lived links to render drafts through the live template
┌──────────────────────────────┐
│ Go binary │
visitor → │ html/template (public SSR) │
admin → │ embedded Svelte (/admin) │
mobile → │ Gin API (/api/v1) │
│ SQLite (cache, auth, dev.) │── git repo (content .md)
└──────────────────────────────┘
Content lives in a git repository; SQLite caches parsed/rendered content and holds auth/session/paired-device state; the web admin SPA is built and embedded into the binary.
| Layer | Choice |
|---|---|
| Backend | Go + Gin, go-git, SQLite (modernc.org/sqlite), JWT |
| Web | Svelte + Tailwind CSS + Milkdown (admin only, embedded) |
| Mobile | Flutter (iOS + Android), same backend API |
| Content | Git repo of .md files — the database |
| Images | GitHub assets repo (CDN) + Cloudflare R2 backup |
Requires Go 1.2x and a content git repo.
cp .env.example .env # set JWT_SECRET, CONTENT_REPO_PATH, etc.
make build # embeds web/dist + builds bin/postify
make run-all # build everything, then run on :3030 (DEV=true)First run: open http://localhost:3030 and complete the one-time /setup wizard.
make web-install # install web dependencies (uses $BUN, default bun)
make web # build SPA + public CSS into web/dist
make web-copy # copy web/dist into internal/webadmin/distFlutter app for authoring on the go — device-pairing onboarding, multi-server support.
cd mobile
make get
make run-ios # or: make run-androidAll backend config is environment variables — see .env.example. Key ones:
| Var | Purpose |
|---|---|
PORT |
HTTP port (default 3030) |
DOMAIN |
Canonical URL/OG domain |
PUBLIC_URL |
Base URL included in device-pairing QR |
JWT_SECRET |
HS256 signing key (>= 32 chars) |
ADMIN_EMAIL |
Optional pre-seeded admin; empty = use /setup |
ADMIN_PASSWORD_HASH |
bcrypt hash, not a raw password |
SQLITE_PATH |
SQLite DB path |
CONTENT_REPO_URL / CONTENT_REPO_PATH |
Content git repo |
GITHUB_TOKEN / GITHUB_ASSETS_REPO |
Image storage (GitHub assets) |
CLOUDFLARE_R2_* |
Image storage backup (R2, dual-written) |
See make help (backend) and mobile/Makefile (mobile).
cmd/server/ Go entrypoint
internal/ backend: handlers, modules (post/page/settings/media/auth/device), web (public SSR), webadmin (embedded SPA)
web/ Svelte admin SPA (bun), Tailwind, public CSS
mobile/ Flutter app (iOS + Android), own Makefile
media/ local dev uploads (production uses GitHub assets/R2)
plan/ PRD/TRD/design docs
Design and planning live in plan/: PRD, TRD (architecture/stack decisions), per-module docs under plan/backend, plan/web, plan/mobile, plan/infra.
- Stabilize the public API contract and version it cleanly
- Hardening pass: auth, pairing, and preview-link edge cases
- Docs site with a guided self-hosting walkthrough