huan

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2026 License: MIT

README

huan

中文 | English

A Go-based static site generator, designed as a Hugo replacement for zhurongshuo.com.

huan builds a static website from Markdown + YAML config + Go templates, producing output that is byte-for-byte comparable with Hugo. It ships as a single binary with zero runtime dependencies, uses the same goldmark Markdown engine as Hugo, and adds first-class support for CJK content, in-page encryption, and a hugo serve-style dev server with LiveReload.


Table of Contents


What is huan?

huan is a static site generator written in Go. Stage 1's goal is to fully replace Hugo for building zhurongshuo.com — every HTML, RSS, sitemap, and search-index byte must match Hugo's output.

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)
  • CJK-aware: word counting, heading IDs, summary truncation all handle Chinese, Japanese, Korean correctly
  • Built-in encryption / redaction: full-page encryption and partial redaction via shortcodes, no plugins required
  • hugo serve-equivalent dev experience: HTTP server + fsnotify file watcher + LiveReload WebSocket, sub-second browser refresh

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 zhurongshuo.com's needs it carries a lot of surface area that goes unused. huan exists to:

  1. Strip Hugo down to the subset zhurongshuo actually uses. No theme system, no taxonomies-of-taxonomies, no multiple output formats beyond HTML/RSS/sitemap/search — just the parts that ship to production.
  2. Treat CJK content as a first-class citizen. hasCJKLanguage, word counting, summary length, and heading ID generation all account for Chinese text without configuration.
  3. Bake encryption into the core. zhurongshuo uses page-level access control (access: protected), random-ratio content redaction, and per-group encryption — these are built into huan rather than bolted on.
  4. Stay verifiable against Hugo. A diff pipeline (scripts/diff-build.sh) byte-compares huan's output against Hugo's. The 905/2028 (44.5%) byte-identical baseline is tracked as a regression gate.
  5. Keep the dev loop fast. huan serve rebuilds atomically (no 404s during rebuild) and broadcasts a LiveReload signal that refreshes the browser within ~1 second of saving a Markdown file.

Features

Commands
Command Purpose
huan build Build the site into publishDir
huan serve Start dev server with file watching + LiveReload

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
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 redact (content redaction with force / show / random / ratio params), 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
  • Minify: HTML / CSS / JS / JSON / SVG / XML via tdewolff/minify
  • canonifyURLs: root-relative URLs post-processed into absolute URLs
  • i18n: YAML-based message bundles (e.g. zh-cn.yaml)
Encryption & redaction
  • access: protected page-level encryption; reads ciphertext from data/encrypted/content.json
  • encryptGroups config (full mode = full-page redaction, random mode = ratio-based random redaction)
  • redact inline shortcode for inline content masking
  • Random redaction uses MD5-seeded deterministic per-character decision (stable across builds)
Dev server internals
  • HTTP static file server with custom 404
  • 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
  • Port-conflict detection with friendly error message
  • 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_0.1.0_<os>_<arch>.tar.gz from /release/0.1.0/ or GitHub Releases
# 2. Verify checksum (optional but recommended):
shasum -a 256 -c huan_0.1.0_checksums.txt   # reports OK for the archive you downloaded
# 3. Extract:
tar xzf huan_0.1.0_darwin_arm64.tar.gz      # produces ./huan, ./LICENSE, ./README*.md
# 4. Move into PATH:
sudo mv huan /usr/local/bin/
huan version                                  # confirm: "huan 0.1.0 (<git sha>)"

Windows users: download the huan_0.1.0_windows_amd64.zip instead and extract huan.exe.

Requires Go 1.26+ for 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/
│   └── _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 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
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

Project Status

Stage 1 (Hugo parity): essentially complete.

  • Milestones 1–9 all landed: CLI / content loading / templates / shortcodes / lists+taxonomy+pagination / auxiliary outputs (RSS, sitemap, search) / minify / verification / dev server
  • Hugo output parity: 905 of 2028 shared files byte-identical (44.5%), 0 files missing, 8 extra files (intentional)
  • 5 known edge-case diffs remain (CJK word segmentation precision, RSS item ordering / description truncation, summary block-level whitespace handling) — see docs/progress/CURRENT_STATE.md for the live status

Stage 2 (plugin architecture): planned, not started.

internal/{pipeline,plugin,search}/ and pkg/ are intentionally absent — they belong to stage 2 and will be created from scratch when that work begins (see docs/technical-plan.md §4.11 for the proposed interface).


Project Structure

huan/
├── cmd/huan/              # CLI entrypoint (main.go, serve.go)
├── internal/
│   ├── build/             # BuildSite core + atomic swap
│   ├── config/            # huan.yaml parser
│   ├── content/           # content loader + tree + frontmatter
│   ├── markdown/          # goldmark pipeline
│   ├── shortcode/         # redact / audio / img
│   ├── encrypt/           # page-level encryption + redaction
│   ├── template/          # html/template loader + funcmap
│   ├── taxonomy/          # tags / categories
│   ├── pagination/
│   ├── output/            # writer + canonify + minify
│   ├── i18n/              # message bundles
│   └── serve/             # HTTP server + watcher + LiveReload
├── 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 1 polish:

  • Close the remaining 5 Hugo-parity edge-case diffs
  • Expand test coverage in internal/{config,content,markdown,output,template,i18n}

Stage 2 — plugin architecture (proposed):

  • AuthPlugin — JWT authentication for protected content
  • PaymentPlugin — paid-content verification
  • MemberPlugin — membership tiers and entitlements
  • DynamicRenderPlugin — HTTP server for dynamic protected-content rendering
  • SearchPlugin — server-side full-text search
  • ContentRelationPlugin — content relationship graph and cross-references
  • CustomTemplatePlugin — pluggable template engine

Plugin interfaces are sketched in docs/technical-plan.md §4.11.


Contributing

Pull requests welcome against the master branch.

Hard rule: every change must keep ./scripts/diff-build.sh at zero new diffs 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.

Directories

Path Synopsis
cmd
equiv-check command
huan command
internal
deploy
Package deploy defines the Deployer capability interface — the first concrete capability in huan's unified plugin system (see docs/adr/0003-unified-plugin-system.md).
Package deploy defines the Deployer capability interface — the first concrete capability in huan's unified plugin system (see docs/adr/0003-unified-plugin-system.md).
deploy/cloudflare
Package cloudflare implements the Cloudflare deploy plugin (Cloudflare Pages direct-upload in PR1; R2 and Worker arrive in later PRs).
Package cloudflare implements the Cloudflare deploy plugin (Cloudflare Pages direct-upload in PR1; R2 and Worker arrive in later PRs).
encrypt
Package encrypt implements full-page content access control: public, protected, private.
Package encrypt implements full-page content access control: public, protected, private.
equiv
Package equiv provides HTML/SEO/AI field comparison utilities for verifying huan output equivalence against Hugo.
Package equiv provides HTML/SEO/AI field comparison utilities for verifying huan output equivalence against Hugo.
i18n
Package i18n loads translation files and resolves keys to translated strings.
Package i18n loads translation files and resolves keys to translated strings.
observability
Package observability provides cross-cutting structured logging used by huan's release/deploy pipelines.
Package observability provides cross-cutting structured logging used by huan's release/deploy pipelines.
output
Package output handles writing rendered HTML and assets to the publish directory.
Package output handles writing rendered HTML and assets to the publish directory.
pagination
Package pagination implements Hugo-style page pagination.
Package pagination implements Hugo-style page pagination.
plugin
Package plugin defines the unified plugin host for huan extensions.
Package plugin defines the unified plugin host for huan extensions.
release
Package release implements the `huan release` command: cross-compile huan for a set of target platforms, archive each with LICENSE/README, compute sha256 checksums, and emit a JSON manifest into /release/{version}/.
Package release implements the `huan release` command: cross-compile huan for a set of target platforms, archive each with LICENSE/README, compute sha256 checksums, and emit a JSON manifest into /release/{version}/.
shortcode
Package shortcode provides Hugo-compatible shortcode registration and expansion.
Package shortcode provides Hugo-compatible shortcode registration and expansion.
taxonomy
Package taxonomy builds tag/category taxonomies from page frontmatter.
Package taxonomy builds tag/category taxonomies from page frontmatter.
version
Package version exposes the huan version string, embedded from the VERSION file at build time.
Package version exposes the huan version string, embedded from the VERSION file at build time.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL