中文 | English
A Go-based local-first single-user content engine — file-based content management with a built-in admin panel and a static site generation pipeline. 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, and LLM-powered translation into the same CLI.
- What is huan?
- Why huan?
- Features
- Quick Start
- Translation & i18n
- Deploy & Release
- Project Status
- Project Structure
- Documentation
- Roadmap
- Contributing
- License
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.yamlfor 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 intohuan 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/.ensidecars 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) andTranslator(Qwen3) ship built-in and share one registry - Self-contained release & deploy:
huan releasefor cross-platform packaging,huan deployfor direct-API Cloudflare publishing, plus GitHub Actions auto-release on tag push hugo 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.
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.mdsidecars (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 serverebuilds atomically (no 404s mid-rebuild) and refreshes the browser within ~1 second of saving a Markdown file.
| Command | Purpose |
|---|---|
huan build |
Build the site into publishDir |
huan serve |
Start dev server with file watching + LiveReload + admin panel at /admin |
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 |
huan serve 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 |
- Markdown: goldmark with
unsafe: trueand 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/templatewith ~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-pageindex.mdmirrors - 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)
- 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.Boolbusy + pending flags) — bursts of edits coalesce into one rebuild - Rebuild errors broadcast as LiveReload
alertmessages; dev server keeps running - Always serves from a temp dir; never touches the real
publishDir
From source (recommended for now):
git clone https://github.com/iannil/huan.git
cd huan
go build -o huan ./cmd/huanVia go install:
go install github.com/iannil/huan/cmd/huan@latestFrom 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.
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: falsemy-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 to publishDir (default: ./public)
./huan build
# Start dev server (default: http://localhost:1313)
./huan serve
# Common serve variations
./huan serve --port 8080 --bind 0.0.0.0 -D
./huan serve --disableLiveReload # no WS, just static files
./huan serve --disableWatch # no rebuild on file change./scripts/diff-build.sh # full rebuild + byte diff vs Hugo
./scripts/diff-summary.sh # structured report only
./scripts/diff-patterns.sh # categorize diffs by patternhuan 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.mdnext 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 qwen3walks every source file, calls a local Qwen3 model over the Ollama HTTP API, and writes.en.mdsidecars. It is incremental (source-hash cached), structure-aware (verifies markdown chunk structure round-trips), and observable (--progress-everyprints throughput + ETA on long runs). - Audit parity.
huan translate auditcrawls a runninghuan 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 issueThe 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.
- Unified plugin system (ADR 0003): capability-based extensions sharing one registry; current capabilities are
DeployerandTranslator. - 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 releasecross-compiles the standard platforms withCGO_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 viago run ./cmd/huan release, and creates a GitHub Release with all tarballs attached.
Current version: v0.4.2.
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.
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/
│ ├── output/ # writer + canonify + minify + AI outputs
│ ├── i18n/ # message bundles + collator + audit + langdetect
│ ├── translate/ # Translator capability + qwen3 provider
│ ├── plugin/ # unified plugin registry
│ ├── deploy/ # Deployer capability + cloudflare provider
│ ├── release/ # cross-compile + packaging
│ ├── equiv/ # Hugo equivalence checks
│ ├── observability/ # structured logging / tracing
│ ├── version/ # build version info
│ └── serve/ # HTTP server + watcher + LiveReload + admin panel mount
├── 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)
docs/INDEX.md— documentation index (start here)docs/technical-plan.md— full architecture blueprintdocs/standards/equivalence.md— the three-dimension equivalence definition & registrydocs/progress/CURRENT_STATE.md— live progress and remaining diffsdocs/adr/— architecture decision records (0001–0009)CLAUDE.md— contributor guide (coding conventions, observability, memory system; Chinese)
Stage 4 — Admin Panel ✅ (shipped in v0.3.0–v0.4.x)
| Component | Status |
|---|---|
| ContentList (search/sort/filter/pagination/batch) | ✅ |
| ContentEdit (Markdown preview, multilingual switch) | ✅ |
ContentNew (auto {title}.{lang}.md naming) |
✅ |
| Settings Page (form + YAML dual mode) | ✅ |
| Dashboard (6-card stats + recent content) | ✅ |
| Multilingual management (badges, filtering, sidecar discovery) | ✅ |
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
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.
MIT © 2026 iannil