huan(幻)
中文 | English
A Go-based local-first single-user content engine — file-based content management with a built-in admin panel, a static site generation pipeline, and a production-grade long-running daemon server. Path toward all-CMS replacement is a v1.x+ roadmap (ADR 0010).
huan turns Markdown + a single YAML config + Go templates into a static website whose output is byte-for-byte verifiable against Hugo (99.7% identical on the reference site, 0 differences on the SEO and AI dimensions). It ships as a single binary with zero runtime dependencies, uses the same goldmark engine as Hugo, treats CJK content as a first-class citizen, and bundles deploy, release, LLM-powered translation, and a production-grade daemon server into the same CLI.
Table of Contents
What is huan?
huan is a local-first single-user content engine written in Go. It was born to fully replace Hugo for building zhurongshuo.com — with the hard requirement that every HTML, RSS, sitemap, and search-index byte be reproducible and verifiable against Hugo's output. With that parity goal essentially met (99.7% byte-identical, 0 differences on SEO and AI dimensions), huan now ships with a built-in admin panel that manages your content right where it lives. The path toward a full CMS replacement (image pipeline, multi-user, asset pipeline) is a v1.x+ roadmap, not a v1.0 commitment.
Key characteristics:
- Single binary, no runtime dependencies, fast cold start
- goldmark for Markdown rendering — the same library Hugo uses
huan.yaml for configuration (YAML, not TOML)
- Built-in admin panel: a content management UI at
/admin — Go JSON API + React SPA with Shadcn UI. Browse, create, edit, and delete Markdown content from your browser; manage site settings (title, description, GA, CDN URL); dashboard with stats and recent content. Integrated into huan serve — zero extra setup.
- CJK-aware: word counting, heading IDs, and summary truncation all handle Chinese / Japanese / Korean correctly without extra configuration
- AI-friendly by default: built-in
llms.txt, content API (/api/{section}.json), and per-page Markdown mirrors — designed for LLM crawlers and AI consumers, not just SEO bots
- Bilingual out of the box: an i18n build system that renders
.zh-cn/.en sidecars into a fully localized site, plus a built-in translator plugin that fills the gaps with a local LLM
- Unified plugin system (ADR 0003): capability-based extensions —
Deployer (Cloudflare) and Translator (Qwen3) ship built-in and share one registry
- Self-contained release & deploy:
huan release for cross-platform packaging, huan deploy for direct-API Cloudflare publishing, plus GitHub Actions auto-release on tag push
huan daemon for production serving: a long-running daemon that serves the site as a backend service — full pre-render + incremental updates + JIT on-demand rendering + a public content-query REST API (/api/v1/*) + real-time SSE push (/api/v1/events) + hot-pluggable .so plugins + admin panel, with TLS, systemd notify, health checks, Prometheus metrics, and graceful shutdown
huan serve-equivalent dev experience: HTTP server + fsnotify watcher + LiveReload WebSocket, sub-second browser refresh
- Verifiable against Hugo: a diff pipeline byte-compares huan's output against Hugo and gates regressions on three dimensions (visual / SEO / AI)
huan is not a drop-in Hugo replacement. Templates are migrated once; afterwards huan owns the build pipeline.
Why huan?
Hugo is excellent, but for a single site's needs it carries a lot of surface area that goes unused. huan exists to:
- Strip the SSG down to the subset that actually ships. No theme marketplace, no output-format matrix beyond HTML/RSS/sitemap/search — just the parts that reach production.
- Treat CJK content as a first-class citizen.
hasCJKLanguage, word counting, summary length, and heading-ID generation all account for Chinese text by default.
- Be AI-friendly by default.
llms.txt, a JSON content API, and per-page Markdown mirrors are generated automatically, so AI agents and LLM crawlers get clean, structured access to your content.
- Make bilingual sites a build-time concern, not a manual chore. Author in one language, drop in
.en.md sidecars (hand-written or LLM-generated), and huan produces a localized site with parity auditing baked in.
- Stay verifiable against Hugo. A diff pipeline (
scripts/diff-build.sh) byte-compares huan's output against Hugo's; the 2026/2032 (99.7%) byte-identical baseline is tracked as a regression gate.
- Keep the dev loop fast.
huan serve rebuilds atomically (no 404s mid-rebuild) and refreshes the browser within ~1 second of saving a Markdown file.
Features
Commands
| Command |
Purpose |
huan build |
Build the site into publishDir |
huan dev |
Start dev server with file watching + LiveReload + admin panel at /admin |
huan daemon |
Start production content server with mixed rendering, API, admin panel, health checks, TLS |
huan serve |
Deprecated — use huan dev instead |
huan new <kind>/<path> |
Scaffold content from archetypes/<kind>.md (multi-archetype) |
huan sync gallery |
Scaffold content/gallery/<name>.md for new images |
huan toc |
Generate TOC markdown for books / practices / products |
huan export |
Export content to a CSV archive (zh_CN sort via i18n collator) |
huan translate qwen3 |
Translate source markdown into .en.md sidecars via a local Qwen3 LLM |
huan translate status |
Report translation state (cached / stale / missing) for all sources |
huan translate audit |
Audit zh/en parity & per-page language correctness against a running serve |
huan deploy cloudflare {pages,r2,worker} |
Deploy to Cloudflare Pages / R2 / Workers |
huan plugin {list,info} |
Inspect registered plugins |
huan release |
Cross-compile + archive + checksums to release/<version>/ |
huan version / env / config / list |
Introspection |
Dev server (huan dev)
huan dev replaces the original huan serve (now deprecated) and provides the same development experience with HTTP static file server, file watching, LiveReload, and admin panel.
huan dev flags:
| Flag |
Default |
Description |
--port |
1313 |
Listen port |
--bind |
127.0.0.1 |
Bind address (supports 0.0.0.0, ::) |
-D / --buildDrafts |
false |
Include draft content |
--disableLiveReload |
false |
Disable browser auto-refresh |
--disableWatch |
false |
Do not watch files for changes |
--debounce |
400ms |
File-change debounce delay |
Rendering pipeline
- Markdown: goldmark with
unsafe: true and configurable typographer; heading IDs aligned with Hugo's algorithm (CJK + Chinese punctuation + HTML entities handled)
- Shortcodes: built-in
audio, img; extensible registry
- Templates: Go
html/template with ~40 Hugo-compatible functions (urlize, safeHTML, markdownify, Scratch, partial, where, sort, index, len, math/string/path helpers, …)
- Taxonomy: tags and categories with list pages and per-term pages
- Pagination:
/page/N/ with /page/1/ redirecting to /
- Outputs: HTML, RSS (per-section / per-taxonomy / per-term),
sitemap.xml, search.json
- AI outputs:
llms.txt, /api/{section}.json, per-page index.md mirrors
- Minify: HTML / CSS / JS / JSON / SVG / XML via
tdewolff/minify
- canonifyURLs: root-relative URLs post-processed into absolute URLs
- i18n: YAML message bundles + a full bilingual build system (ADR 0007)
Daemon server (huan daemon)
huan daemon is the production-grade long-running server. It starts with a full build, then serves the site with:
- Static file server — serves pre-rendered HTML from the initial build with zero latency
- Incremental rebuild — content changes (via file watcher or Admin API) trigger a DAG-based incremental build that only rebuilds affected pages
- JIT on-demand rendering — pages not yet pre-rendered (drafts, build-skipped) are rendered on-demand and cached (LRU + TTL), reusing the cached pipeline state
- Content query REST API — public read-only endpoints at
/api/v1/pages, /api/v1/pages/{url}, /api/v1/tags, /api/v1/sections (filter by section/tag/full-text, paginate, sort)
- Real-time SSE push —
/api/v1/events streams daemon events (build completed, content changed, plugin loaded) to browsers via Server-Sent Events
- Hot-pluggable plugins — load/unload/reload
.so plugins at runtime via Admin API or CLI
- Admin API — full content management at
/admin/
- Health check —
/health endpoint with 503 not-ready signal during initial build
- Prometheus metrics —
/metrics endpoint with build duration, request count, cache hit/miss
- TLS — optional TLS cert/key for HTTPS
- systemd notify —
sd_notify protocol for service management
- Graceful shutdown — 30s timeout for in-flight requests
- Periodic full rebuild — configurable interval for complete site regeneration
huan daemon flags:
| Flag |
Default |
Description |
--port |
8080 |
Listen port |
--bind |
0.0.0.0 |
Bind address |
--config |
"" |
Daemon config file path (daemon.yaml) |
--tls-cert |
"" |
TLS certificate path |
--tls-key |
"" |
TLS private key path |
--systemd |
false |
Enable systemd notify integration |
--plugin-dir |
<sourceDir>/plugins |
Directory for .so plugins |
--disable-plugin |
false |
Disable plugin loading |
-D / --buildDrafts |
false |
Include draft content |
Dev server (huan dev)
huan dev replaces the original huan serve (now deprecated) and provides the same development experience:
- HTTP static file server with custom 404
- Admin panel at
/admin: live content management UI (read/write markdown files, edit settings, manage media)
- Recursive fsnotify watcher with configurable debounce
- LiveReload WebSocket hub with per-client broadcast channels (slow clients don't block)
- Atomic rebuild: writes to a sibling staging directory, then
rename(2) swaps it in — old content stays served during multi-second rebuilds, no 404s
- Serialized rebuilds (
atomic.Bool busy + pending flags) — bursts of edits coalesce into one rebuild
- Rebuild errors broadcast as LiveReload
alert messages; dev server keeps running
- Always serves from a temp dir; never touches the real
publishDir
Quick Start
Install
From source (recommended for now):
git clone https://github.com/iannil/huan.git
cd huan
go build -o huan ./cmd/huan
Via go install:
go install github.com/iannil/huan/cmd/huan@latest
From a release tarball (no Go toolchain required):
# 1. Download huan_<version>_<os>_<arch>.tar.gz from /release/<version>/ or GitHub Releases
# 2. Verify checksum (optional but recommended):
shasum -a 256 -c huan_<version>_checksums.txt
# 3. Extract:
tar xzf huan_<version>_darwin_arm64.tar.gz # produces ./huan, ./LICENSE, ./README*.md
# 4. Move into PATH:
sudo mv huan /usr/local/bin/
huan version # confirm: "huan <version> (<git sha>)"
Windows users: download huan_<version>_windows_amd64.zip and extract huan.exe.
Requires Go 1.26+ for the go install / go build paths; pre-built tarballs have no Go dependency.
Minimal huan.yaml
baseURL: "https://example.com/"
title: "My Site"
languageCode: "zh-cn"
publishDir: "public"
paginate: 10
hasCJKLanguage: true
summaryLength: 120
markup:
goldmark:
renderer:
unsafe: true
extensions:
typographer: false
Content layout
my-site/
├── huan.yaml
├── content/
│ ├── posts/
│ │ └── 2026/
│ │ └── 06/
│ │ └── hello.md # → /posts/2026/06/hello/
│ │ └── hello.en.md # → /en/posts/2026/06/hello/ (optional sidecar)
│ └── _index.md # home page
├── layouts/ # Go html/templates
│ ├── _default/
│ │ ├── single.html
│ │ └── list.html
│ └── partials/
└── static/ # copied verbatim
Build & serve
# Build to publishDir (default: ./public)
./huan build
# Start dev server (default: http://localhost:1313)
./huan dev
# Start production daemon server (default: http://0.0.0.0:8080)
./huan daemon
# Common dev variations
./huan dev --port 8080 --bind 0.0.0.0 -D
./huan dev --disableLiveReload # no WS, just static files
./huan dev --disableWatch # no rebuild on file change
Verify against Hugo (regression gate)
./scripts/diff-build.sh # full rebuild + byte diff vs Hugo
./scripts/diff-summary.sh # structured report only
./scripts/diff-patterns.sh # categorize diffs by pattern
Translation & i18n
huan turns a bilingual site into a build-time concern (ADR 0007, ADR 0008):
- Author once. Write your content in the source language (e.g.
hello.md).
- Add sidecars. Place a
hello.en.md next to it — hand-written, or generated by a translator plugin. huan renders it under a localized URL prefix (e.g. /en/...).
- Fill the gaps with an LLM.
huan translate qwen3 walks every source file, calls a local Qwen3 model over the Ollama HTTP API, and writes .en.md sidecars. It is incremental (source-hash cached), structure-aware (verifies markdown chunk structure round-trips), and observable (--progress-every prints throughput + ETA on long runs).
- Audit parity.
huan translate audit crawls a running huan serve, enumerates the zh and en sitemaps, and reports missing/orphan sidecars plus per-page language correctness (English pages with untranslated Chinese, or vice-versa). Read-only; never touches content.
# Translate everything that's new or changed (incremental)
./huan translate qwen3
# Preview what would be translated, without calling the LLM
./huan translate qwen3 --dry-run
# Translate a single file, forcing a re-run
./huan translate qwen3 --file posts/2026/06/hello.md --force
# Report cached / stale / missing translation state
./huan translate status
# Audit zh/en parity against a running dev server
./huan serve &
./huan translate audit --fail # exit non-zero if any parity/language issue
The Translator capability is part of the unified plugin system, so additional providers (cloud APIs, other local models) can be added under internal/translate/<provider>/ without touching the build pipeline.
Deploy & Release
- Unified plugin system (ADR 0003): capability-based extensions sharing one registry; current capabilities are
Deployer and Translator.
- Cloudflare deploy (ADR 0002): pure-Go direct API (no wrangler shell-out). Pages uses blake3 hash + direct upload, R2 uses minio-go (S3-compatible, MD5 etag), Worker uses the multipart modules API. Deploys are self-contained (ADR 0009).
- Local packaging (ADR 0004):
huan release cross-compiles the standard platforms with CGO_ENABLED=0 -trimpath -ldflags=-s -w, producing tarball/zip + sha256 checksums + a JSON manifest. Deterministic: same commit + Go version → identical sha256.
- CI auto-release (ADR 0005): a GitHub Actions workflow runs on
v* tag push, builds artifacts via go run ./cmd/huan release, and creates a GitHub Release with all tarballs attached.
Project Status
Current version: v0.6.0.
Stage 1 (Hugo parity): complete. On the reference site (zhurongshuo.com), the three-dimension equivalence gate passes:
| Dimension |
Result |
Status |
| SEO |
0 differing |
✅ PASS |
| AI |
0 differing |
✅ PASS |
| Byte-identical files |
2026 / 2032 |
99.7% |
| Files only in Hugo / only in huan |
0 / 0 |
✅ |
The remaining handful of normalized/byte diffs are all either chroma syntax-highlighter version differences (huan ships chroma v2.26.1 vs Hugo's bundled version) or non-visible artifacts (RSS description indentation, sitemap URL ordering). Visual, SEO, and AI dimensions are all difference-free. See docs/standards/equivalence.md and docs/progress/CURRENT_STATE.md for the live status.
Stage 2 (AI-friendly outputs + i18n + translation): shipped. llms.txt, content API, Markdown mirrors, the bilingual build system, and the Qwen3 translator plugin all landed across the v0.2.x–v0.3.x line.
Project Structure
huan/
├── cmd/
│ ├── huan/ # CLI entrypoint (main.go + per-command files)
│ └── equiv-check/ # equivalence-check helper binary
├── internal/
│ ├── admin/ # Go JSON API for admin panel (content CRUD + settings + media)
│ ├── build/ # BuildSite core + atomic swap
│ ├── config/ # huan.yaml parser
│ ├── content/ # content loader + tree + frontmatter
│ ├── markdown/ # goldmark pipeline
│ ├── shortcode/ # audio / img
│ ├── template/ # html/template loader + funcmap
│ ├── taxonomy/ # tags / categories
│ ├── pagination/
│ ├── daemon/ # Production daemon server (Builder, Serving, Health, Metrics, Lifecycle)
│ │ ├── cache/ # JITCache (LRU + TTL), RenderCache
│ │ ├── dag/ # Dependency Graph (incremental rebuild)
│ │ └── eventbus/ # EventBus (Builder/Serving decoupling)
│ ├── deploy/ # Deployer capability + cloudflare provider
│ ├── dev/ # Dev server (HTTP + LiveReload + watcher — replaces old serve/)
│ ├── translate/ # Translator capability + qwen3 provider
│ ├── plugin/ # unified plugin registry
│ ├── release/ # cross-compile + packaging
│ ├── equiv/ # Hugo equivalence checks
│ ├── observability/ # structured logging / tracing
│ ├── version/ # build version info
│ ├── output/ # writer + canonify + minify + AI outputs
│ ├── i18n/ # message bundles + collator + audit + langdetect
├── web/
│ └── admin/ # React SPA (admin UI — Vite + Shadcn UI + Tailwind)
├── scripts/ # diff-build.sh + diff-summary.sh + diff-patterns.*
├── docs/ # see docs/INDEX.md
├── memory/ # project memory (MEMORY.md + daily notes)
├── huan.yaml # example config
├── go.mod / go.sum
└── CLAUDE.md # contributor guide (Chinese)
Documentation
Roadmap
Stage 5 — Daemon Server ✅ (shipped in v0.6.0)
huan daemon is a production-grade long-running content server:
| Component |
Status |
| Builder + Serving architecture |
✅ |
| EventBus (Builder/Serving decoupling) |
✅ |
| Full build on startup |
✅ |
| Static file server (pre-rendered HTML) |
✅ |
| DAG-based incremental rebuild |
✅ |
| JIT fallback rendering + LRU+TTL cache |
✅ |
| File watcher (fsnotify) |
✅ |
| Build queue (serialize + coalesce pending) |
✅ |
| Admin API |
✅ |
| Health check (503 not-ready → 200 ready) |
✅ |
| Prometheus metrics |
✅ |
| TLS support |
✅ |
| systemd notify |
✅ |
| Graceful shutdown (30s timeout) |
✅ |
| Periodic full rebuild (configurable interval) |
✅ |
| Test coverage: 47.1% (daemon) / 82.1% (template) / 86% (cache) / 83% (DAG) |
✅ |
Next up:
- Admin authentication (session/token)
- Media library management (upload, crop, gallery)
- User & permission management
- CMS migration tools (WordPress / Ghost → huan)
- Admin-integrated deploy configuration
Contributing
Pull requests welcome against the master branch.
Hard rule: every change must keep ./scripts/diff-build.sh at zero new diffs on the SEO/AI dimensions vs Hugo (or explicitly document expected diffs in the PR description and docs/progress/CURRENT_STATE.md).
Workflow:
# 1. Make your change
# 2. Verify
go build -o huan ./cmd/huan
go test ./...
./scripts/diff-build.sh
# 3. Commit (small, focused commits preferred)
For coding conventions, observability requirements, and the memory system, read CLAUDE.md before contributing.
License
MIT © 2026 iannil