This is the multi-page printable view of this section. .
Get started
- 1: Use OINK Starter
- 2: Starter repository tour
- 3: From scratch and other install methods
- 4: OINK CLI capabilities and next steps
- 5: Use the OINK CLI
The recommended path for a new site starts from
pgsty/oink-starter, not from a copy
of this documentation and regression repository. The Starter is a public
GitHub template: it pins a published OINK release, builds as-is, and contains
only neutral project content and deployment workflows.
OINK’s declared compatibility floor is Hugo Extended 0.160.1. The current Starter and its CI use Hugo Extended 0.165.0 and Go 1.27. Use that pinned Starter toolchain for the path below; use the lower floor only when maintaining an existing site that deliberately supports it.
Choose a path
| Starting point | Recommended path | Result |
|---|---|---|
| New documentation or project site | OINK Starter | A small three-language Docs, Blog, and Book site with two deployment workflows |
| Existing Hugo site | From scratch | Add the OINK module and required Goldmark settings without replacing content |
| Existing Docsy or older OINK site | Upgrade | Preserve content, migrate supported syntax, and review site overrides |
Five-minute baseline
-
Install the tools
Install Git, Go 1.27 or newer, and Hugo Extended 0.165.0 or newer. The Hugo output must contain
extended:On macOS,
brew install git go hugosupplies them. On Linux and Windows, use the official Hugo installation guide and Go downloads; choose Hugo Extended. -
Create or clone the site
For a repository you intend to keep, open the Starter and select Use this template, then clone the repository GitHub created for you. To evaluate the untouched original locally:
-
Open the baseline
Open http://localhost:1313/. The default Starter also publishes Chinese at
/zh/and French at/fr/. Confirm that Docs, Blog, Book, search, language switching, and light/dark mode all work before editing anything. -
Make one visible change
Change the title and canonical URL at the top of
hugo.yaml, then edit one sentence indata/home/en.yaml. A browser reload that shows both changes is the first useful proof that configuration, content, and the pinned theme are connected correctly.
Customize from shallow to deep
- Use OINK Starter — choose languages first, then identity, home page, content, navigation, brand, integrations, and deployment.
- Starter repository tour — which file owns each part of the site, what to replace, and what can be removed.
- Writing pages — front matter, headings, links, images, drafts, and the page-end controls.
- Components — add expression only after the content tree is stable.
- Brand and appearance — logo, accent, typography, width, and CSS extension points.
- Deploy — use the supplied GitHub Pages or Cloudflare Pages workflow, then verify the real public routes.
This order is deliberate. A site that first proves its build and content tree is easier to debug than one that changes languages, navigation, CSS, analytics, and hosting at the same time.
Publication gate
Before the first push, run the same warning-strict production build the Starter workflows use:
Success means the command ends with Total in …, prints no warning or error,
and public/ contains the language roots and representative Docs, Blog, and
Book routes. It does not yet prove deployment: a local build, a commit, a push,
a green workflow, and correct public rendering are separate gates.
Next
Start with the complete Starter tutorial. If the template deliberately carries more structure than your project needs, use the repository tour to remove it safely. Use From scratch only when adding OINK to an existing site or when you explicitly want to assemble every file yourself.
1 - Use OINK Starter
pgsty/oink-starter is the supported
starting point for a new OINK site. It is deliberately smaller than
oink.pgsty.com: no theme documentation, analytics account, comment
repository, browser regression suite, or PGSTY-specific brand is copied into
your project.
As of 2026-09-20, the template pins OINK v1.0.0, Go 1.27, and Hugo Extended 0.165.0. Its default three-language, English-only, and English–Chinese profiles have all been built warning-strictly against that release.
What the template contains
| Surface | Included baseline | First decision |
|---|---|---|
| Languages | English, Simplified Chinese, French | Keep all three, or select a supplied single/bilingual profile |
| Content | Docs, Blog, and a short Book tutorial | Rewrite the examples; delete a whole surface only when you do not need it |
| Home | One compact data/home/<lang>.yaml per language |
Replace the project promise and destinations |
| Brand | Neutral logo and favicon | Keep them until real project artwork exists |
| Integrations | Repository, Giscus, analytics, share, and feedback examples are commented | Enable only complete configurations you intend to operate |
| Deployment | GitHub Pages and Cloudflare Pages Direct Upload workflows | Choose one production path and verify its real URL |
The Starter’s own Book at /book/ is a four-chapter tour from preview to
deployment. This page is the maintainer-grade version: it explains the order of
changes, the boundaries between them, and the checks after each layer.
Create your repository
GitHub template, recommended
Open the Starter repository, select Use this template → Create a new repository, then clone the repository created under your account or organization:
This gives your site its own Git history and keeps the original Starter as an upstream reference rather than as a remote you might accidentally push to.
Clone the original to evaluate it
For a disposable local evaluation:
Do not start a real project by deleting this clone’s .git directory. GitHub’s
template operation already creates the clean project boundary and preserves an
auditable first commit.
Preview before changing anything
Open these routes:
/,/zh/,/fr/— the three home pages;/docs/,/blog/,/book/— the three content surfaces;- one translated page, then the language switcher;
- search and the light/dark control at a narrow viewport.
Also record the resolved module:
The template snapshot 137843b documented here pins
github.com/pgsty/[email protected]; if the template has since changed, use the version
in your clone’s go.mod. This unchanged preview is your baseline. After that
first success, upgrade a site still using 1.0 as a separate step with the
1.0-to-1.1 checklist, before customizing content.
Customize in layers
Layer 1: language profile
The root configuration enables English, Chinese, and French. Before making other configuration edits, choose one of the supplied profiles when that is not your intended language set. Choose one of these commands:
These are complete minimal configurations, not fragments: copying one replaces
the commented integration examples in the root file. Do it at the beginning;
if hugo.yaml already contains project changes, merge the languages and
disableLanguages sections instead of overwriting it.
If you already changed the title and URL in Get started, keep that file. To
change the default three-language setup to English and Chinese, add only
disableLanguages: [fr] at the top level; use [zh, fr] for English only.
This preserves the identity and integration settings you have already changed.
Disabled languages stay declared so Hugo recognizes .zh.md and .fr.md as
translations and safely ignores them. If you remove a language permanently,
remove its content and home data only after the selected profile builds.
Layer 2: identity
Change the two marked values at the top of hugo.yaml:
The YAML anchor carries the title to all enabled languages. Then change the copyright holder and, after the new repository exists, uncomment its links:
Run hugo server again and check the browser title, footer, edit/history links,
and canonical URL. Do not change the logo yet unless the project has final
artwork; text identity is easier to review first.
Layer 3: home page
The home page is data rather than an opaque layout override:
Edit one language first. In each file, sections fixes the order; hero,
cards, and cta provide the content. Replace the promise, destination URLs,
and sample card copy while keeping the structure. After the first language is
right, translate the same information into the enabled peers.
For another composition, use the full registry in Home and landing pages; do not copy the Starter home partial, because there is no site-specific template to copy.
Layer 4: content and navigation
Rewrite or remove sample leaf pages under content/. Keep section roots until
you decide whether that whole surface belongs in your project:
The content tree becomes the sidebar. Top navigation lives in menus.main on
the translated _index roots, so renaming Docs, Blog, or Book happens beside
the content it names rather than in a second global menu tree. Keep translated
files side by side and give corresponding headings the same explicit IDs:
Follow Organizing content before creating a custom navigation data file; the generated tree is enough for most sites.
Layer 5: brand and reader features
Replace assets/icons/logo.svg and static/favicon.svg when real assets are
ready. Then enable the smallest useful configuration changes, one at a time:
For custom local fonts, use params.ui.fonts for family names or declare font
files in site CSS. For layout, sidebar, search, and component settings, consult
the Configuration reference rather than copying the
much larger configuration of oink.pgsty.com.
Layer 6: integrations
The Starter leaves repository actions, Giscus, Google Analytics, feedback, and sharing off or commented. Enable an integration only after all of its required facts are known:
- repository links need the real owner, repository, and branch;
- Giscus needs its repository/category names and immutable IDs;
- Google Analytics needs a project-owned measurement ID;
- feedback records structured
gtagevents only when analytics is present; - assistant links send the current URL to a third party and therefore require an explicit policy choice.
An incomplete optional block should remain commented. See Comments, Analytics and SEO, and Repository links for the operating boundary of each integration.
Build and deploy
Strict local build
Before enabling a hosting workflow:
Commit hugo.yaml, go.mod, and go.sum; never commit generated public/,
resources/, module caches, or a local module replacement.
GitHub Pages
The Starter already contains .github/workflows/github-pages.yaml. In
Settings → Pages, select GitHub Actions as the source. A push to main
builds with the pinned toolchain, asks GitHub for the correct project subpath,
and publishes public/ through the Pages deployment API.
Cloudflare Pages
The supplied .github/workflows/cloudflare-pages.yaml uses Direct Upload.
Create a Pages Direct Upload project, add CLOUDFLARE_ACCOUNT_ID and
CLOUDFLARE_API_TOKEN, then run the workflow manually once. Set the repository
variable CLOUDFLARE_PAGES_ENABLED=true for automatic deploys, and
CLOUDFLARE_SITE_URL when the canonical address is not the default
pages.dev domain.
Use either Direct Upload or Cloudflare Git integration for one project, not
both. The complete host comparison and baseURL rules are in
Deploy.
Verify and remove samples
Before calling the site ready:
- Search for placeholders such as
Project Name,example.org,OWNER, andPROJECT, then decide whether each remaining occurrence is intentional. - Open every enabled language root and representative Docs, Blog, and Book pages on desktop and mobile.
- Confirm language switching lands on peers, not the home page.
- Test search, dark mode, one component, Markdown output, print, 404, canonical URLs, and repository actions.
- Check the deployed workflow and the public URL separately from the local build.
Delete the sample Book or Blog only after removing its top-menu root and any home-page card that links to it. A warning-strict rebuild after each whole surface is removed keeps failures attributable to one change.
Next
Use the Starter repository tour as a file-level map, then continue with Writing pages and Configuration. For an existing site that should not inherit the Starter’s content model, use From scratch.
2 - Starter repository tour
This page describes the repository created from
pgsty/oink-starter. It is not a tour
of the much larger oink.pgsty.com documentation and regression repository.
The theme source is not copied into either site: go.mod pins it as a Hugo
Module, and Hugo stores the resolved source in the Go module cache.
Top-level map
oink-starter/
- oink-starter/
- hugo.yamlidentity, languages, outputs, parameters, module import
- go.modsite module and exact OINK release
- go.summodule checksums
- examples/
- hugo.single.yamlEnglish-only complete profile
- hugo.bilingual.yamlEnglish + Chinese complete profile
- data/
- home/
- en.yamlone compact landing page per language
- zh.yaml
- fr.yaml
- home/
- content/
- _index.mdlanguage home roots
- _index.zh.md
- _index.fr.md
- docs/Introduction, Get Started, Tutorial, Reference
- blog/posts, design records, release announcements
- book/sequential tutorial about the Starter
- assets/
- icons/logo.svgprocessed project logo
- static/
- favicon.svgcopied unchanged to the site root
- i18n/
- fr.yamlStarter-specific French interface overrides
- .github/workflows/
- github-pages.yamlstrict build and GitHub Pages deployment
- cloudflare-pages.yamlstrict build and Cloudflare Direct Upload
- README.mdoperating summary for repository maintainers
- LICENSEtemplate source license
Generated public/, resources/, .hugo_build.lock, and module caches are
ignored build state, not source.
What to change first
| Path | Responsibility | Initial action |
|---|---|---|
hugo.yaml |
Identity, canonical URL, languages, outputs, theme features, optional integrations | Change the two marked values; choose a language profile before other edits |
data/home/ |
Home-page promise, cards, calls to action | Rewrite every enabled language after one language is approved |
content/ |
All reader-facing material | Replace example leaves; keep a section root until deciding to remove that whole surface |
assets/icons/logo.svg |
Processed logo | Replace only with final artwork |
static/favicon.svg |
Browser icon | Replace together with the logo review |
params.github_* in hugo.yaml |
Edit/history/new-page/issue links | Uncomment only after the destination repository exists |
What to keep
go.modandgo.sum: together they pin and verify the template’s selected OINK release. Commit both; keep that baseline distinct from a later upgrade.- The three Goldmark settings in
hugo.yaml: native Steps, Cards, Fields, image attributes, and Book targets depend on them. outputs: removingmarkdown,LLMS, orprintintentionally removes the corresponding Markdown, agent-index, or print surfaces.fetch-depth: 0in workflows whenenableGitInfostays on: last-modified and contributor facts need repository history.GOWORK: offandHUGO_MODULE_WORKSPACE: offin CI: a developer’s local workspace must not replace the published release being verified.
Optional surfaces
Docs, Blog, and Book are independent top-level surfaces. To remove one safely:
- delete its
content/<surface>/tree; - remove any home-page card or link that targets it;
- confirm no other page links to it;
- run a warning-strict build and inspect the remaining top navigation.
Do not delete only translated section roots: that creates language-specific navigation and fallback behaviour that is difficult to distinguish from a mistake. Remove a surface in all enabled languages or document the asymmetry.
The two configuration profiles under examples/ are optional after the
language decision. They are useful references, but the root hugo.yaml is the
only active site configuration.
Content and navigation
Under Docs and Book, directory structure and weight form the sidebar and
pager sequence. Top navigation comes from menus.main on section roots. A
translated root repeats the same identifier, parent, and weight while
translating visible labels.
The Starter intentionally demonstrates the Documentation System model:
- Introduction explains what and why;
- Get Started gets a new user to a result;
- Tutorial teaches an end-to-end task;
- Reference records exact supported behaviour.
Rename or reshape those sections for the project, but preserve the separation between learning paths rather than mixing every kind of answer into one tree.
Language model
English source files end in .md; Chinese and French peers end in .zh.md and
.fr.md. Home data uses language keys under data/home/. The root profile
declares the languages, their locale, order, and site description.
The single and bilingual profiles keep disabled languages declared. This is intentional: Hugo then recognizes the unused suffixes as translations instead of rendering several files onto one English URL. Copy a profile only before project-specific configuration begins; afterwards merge changes by hand.
Where OINK lives
Two files establish the module boundary:
This is the 137843b Starter snapshot used by the tutorial, not the latest
OINK release. For a newer template, read its own go.mod.
hugo mod graph shows the resolved version. Production follows the exact tag
in go.mod; a local HUGO_MODULE_REPLACEMENTS value is a development override
and must never be committed or treated as release proof.
Deployment files
The GitHub Pages workflow runs automatically on pushes to main; repository
settings must select GitHub Actions as the Pages source. The Cloudflare workflow
runs manually, or automatically only after the repository variable
CLOUDFLARE_PAGES_ENABLED=true is set. Its required account ID and API token
remain repository secrets.
Keep only the workflows for deployment paths you operate. Cloudflare Direct Upload and Cloudflare Git integration are alternative ownership models for the same project, not two gates to run together.
Safe customization order
- Prove the untouched preview.
- Select languages, then change identity.
- Replace one home page and then its translations.
- Replace content and verify navigation.
- Change brand and reader features one group at a time.
- Enable complete external integrations.
- Run the strict production build.
- Deploy, then verify production independently.
Commit between layers when the repository is already yours. Small boundaries make a later regression or rollback attributable to one decision.
Verify
The module graph names the pinned release, the build emits no warning or error,
and Git status contains source edits but no public/ or cache files. Then open
the enabled language roots and one Docs, Blog, and Book route before moving to
deployment.
Related
- Use OINK Starter — the complete layered workflow
- From scratch — add OINK without adopting this content model
- Organizing content — sidebar, pager, and menu authority
- Configuration — every current site parameter
- Deploy — host-specific setup and production checks
3 - From scratch and other install methods
This is the manual alternative to the recommended
OINK Starter. It builds a minimal site in an empty
directory: a small hugo.yml plus one hugo mod get gives a single-language
site you can preview. The cost is that the home page, example content,
deployment workflow, and every component usage are yours to assemble.
For an existing Hugo site, use the short integration path below. For an existing Docsy site, see Upgrade.
The second half weighs four install methods: Hugo Module, Git submodule, offline archive, and pinned source copy. OINK 1.1.0 uses Go 1.27 and Hugo Extended 0.165.0 for release validation. The theme’s lower declared compatibility floor is for existing sites that deliberately retain an older toolchain.
Add OINK to an existing site
Work on a branch with the site’s current configuration and content preserved.
Skip hugo new site and keep the existing configuration filename.
- If the site has no
go.mod, runhugo mod initwith your repository’s module path. Otherwise keep the existing module declaration. - Run
hugo mod get github.com/pgsty/[email protected]. - Replace the old theme selection with the OINK
module.importsentry shown below; preserve unrelated imports and configuration. Merge the threemarkup.goldmarksettings andmarkup.highlight.noClasses: falsefrom the example. Do not replace your whole configuration with it. - Review site-owned
layouts/and assets, old theme shortcodes, and pagetype/layoutvalues: those overrides and conventions may still select the previous theme’s behavior. Keep content and make only the adaptations needed. - Run
hugo --panicOnWarning, then open an existing representative page withhugo server. Check its navigation, images, and code blocks before applying optional OINK features. Continue with verification.
From an empty directory to the first page
-
Create the skeleton and fetch the theme
What follows
hugo mod initis your own site’s module path, usually the repository address.hugo mod getwritesgo.modandgo.sum, and both are committed.Before building, create
.gitignoreso generated files stay out of Git. LeaveenableGitInfooff until you have made the first commit:.gitignoreThe newest version number is on GitHub Releases; the
v1.2.0on this page is what this site currently pins. A production site pins a release tag rather than followingmain:@latestis a one-off resolution, not a version policy. -
Writing
hugo.ymlFor this new site only, rename the generated
hugo.yamltohugo.yml(Hugo accepts both) and replace its contents with the following. Existing sites should merge the relevant settings instead:hugo.ymlWhat each of the five blocks governs:
Block Governs Consequence of omitting it Top level + languagesSite name, domain, languages and navbar menu A wrong baseURLsends every absolute link astray in productionmarkup.goldmarkThe three component prerequisites An attribute line becomes a literal {.steps}in the proseparamsSearch, repository links, shell switches Interactive features stay off; the theme does not decide for the site outputsThe per-page .md,llms.txtand print pagesNo “Copy as Markdown” in the page menu, and no print view moduleReferences the theme and declares the Hugo floor The build cannot find the theme Mathematics additionally needs Goldmark’s passthrough extension; see Math. Every key’s full meaning and default is in Configuration.
-
Write the first page
Every top-level directory under
content/is a section, and the directory structure is the sidebar structure. A documentation section needs at least an_index.md:content/docs/_index.mdcontent/docs/install.mdWrite explicit
{#id}anchors on headings: when a translation is added later, the two languages’ anchors have to correspond. How to write a page is in Writing pages. -
Preview
Open http://localhost:1313/docs/; the Docs section lists Install. The home page is still empty until you add home content. Edit the Install page and confirm that the preview updates.
Other install methods
The steps above use a Hugo Module. The other three address particular
constraints: network isolation, a platform that requires the build input to
contain the whole theme tree, or an organization that reviews its own copy of
the theme. Apart from hugo mod vendor, none of them creates a Go module, and
the site references the theme with theme: oink rather than module.imports.
The shared cost is that version resolution and integrity checking become your
responsibility.
Hugo Module (recommended)
The only method where Hugo resolves the version itself, verifies the checksum,
and leaves an audit record in go.sum. hugo mod graph shows what actually
resolved and hugo mod get -u upgrades. It needs Go on the machine.
Git submodule
Record an exact theme commit in the site repository:
CI must initialize the submodule before running Hugo, or themes/oink is an
empty directory:
Offline archive
For network-isolated environments. Two paths, both prepared on a connected machine and carried in whole.
With hugo mod vendor, the resolved theme source is frozen into the site
directory, and later builds need neither the network nor Go.
When _vendor/ exists Hugo prefers it (hugo mod graph prints +vendor), and
module.imports in hugo.yml stays as it is. This step needs Go; the builds
after it do not. Upgrading the theme means returning to a connected environment
and running hugo mod get and hugo mod vendor again.
_vendor/ collects only the directories the theme mounts (assets, data,
i18n, layouts, static) plus hugo.yaml and theme.toml. It does not
include LICENSE, NOTICE or VENDOR.json. To redistribute that archive, take
those three files from the theme repository as well.
With a tag source archive, no Go module is created; a version of the theme is
simply unpacked into themes/oink/.
The theme repository’s root is the module root, so unpacking lands directly on
layouts/, assets/, i18n/ and static/ with no further level to descend
into. Redistribution must keep LICENSE, NOTICE and VENDOR.json; the last
records each third-party runtime’s version, source, licence path and SHA-256,
and is what an offline audit rests on.
When moving between machines, generate the archive and its checksum from an immutable tag on the connected side:
Carry the archive and its .sha256 into the isolated environment, verify, then
unpack:
An archive produced this way is your own artifact, not a project release. Whether a given tag’s release page carries an archive and a checksum file varies by release; verify the checksum independently when using a public attachment.
Before building offline, confirm the archive is complete. All eleven of these must be present:
themes/oink/
- oink/
- go.modmodule path declaration, used when resolving as a Hugo Module
- hugo.yamltheme default parameters and the Hugo version floor
- theme.tomltheme metadata, required by the theme: oink method
- LICENSEApache-2.0
- NOTICEupstream attribution; must be kept on redistribution
- VENDOR.jsonthird-party runtime manifest: version, source, licence path, SHA-256
- assets/SCSS, JS and the third-party runtimes shipped with the theme
- layouts/templates, partials, shortcodes, render hooks
- static/font files, published as is
- i18n/32 interface language files
- data/the SPDX licence table behind the page-end attribution line
Pinned source copy
When a hosting platform needs the theme files in the site repository, use the
tag archive procedure above and unpack it into
themes/oink/. Set theme: oink and commit the extracted files together with
the tag and checksum you verified.
A plain git clone ... themes/oink leaves a nested .git directory. Adding it
to the parent repository records a Git link, not the theme files; it therefore
does not provide this self-contained source copy. Use a submodule if you want
Git to track the theme by reference.
The four methods compared
| Method | Needs Go | Version auditable | Theme source in your repository | Use when |
|---|---|---|---|---|
| Hugo Module | Yes | go.sum verifies automatically |
No | The default |
| Git submodule | No | The repository records the commit | By reference | The theme source has to be in the repository |
| Offline archive | No | Checksums verified by hand | Yes | Network isolation |
| Pinned source copy | No | Record the tag and checksum | Yes | The platform requires a complete tree |
Bootstrap, Font Awesome, the fonts, and the search and diagram runtimes all
ship with the theme. A site needs no node_modules, no PostCSS, no RTLCSS and
no CDN. Tutorials that install npm dependencies for a Docsy site describe
upstream Docsy’s process and do not apply to OINK.
Developing against a local theme checkout
This section applies only when changing the theme and the site together. Clone the two repositories as siblings:
Use the HUGO_MODULE_REPLACEMENTS environment variable to substitute the local
checkout temporarily, leaving go.mod untouched:
The documentation site’s Makefile is an alias for exactly these commands, and
make dev and make check expect the theme checkout at the sibling ../oink:
A Go workspace (go work init plus HUGO_MODULE_WORKSPACE=go.work) is an
equivalent alternative. Both apply to the local machine only: CI and production
builds use the version in go.mod, and go.work is never committed.
Verify
It passes when the build ends with Total in … and no WARN or ERROR. Then
confirm:
/docs/opens and the sidebar holds the page you wrote- The navbar has a search box that finds the heading you just wrote
- The light/dark toggle is present, and code block colours follow it (which shows
markup.highlight.noClasses: falsetook effect) git status --shortlists only source changes; generated output is ignored. For the Module path, commit bothgo.modandgo.sum; other install methods keep their own theme source or submodule record.
Related
- Get started — choose between Starter, an existing Hugo site, and migration
- OINK Starter — the recommended new-site path
- Starter repository tour — what each template directory owns
- Configuration — every
hugo.ymlkey and its default - Writing pages — how to keep writing after the first page
- Upgrade — upgrading the theme module, and migrating from Docsy
4 - OINK CLI capabilities and next steps
This page retains the 2026-09-30 command inventory and evidence. Its six-command, platform and suggested-CI statements describe that date. Current maintenance behavior belongs to the CLI contract and usage guide; the maintenance record preserves earlier acceptance for its identified source and binaries. Local acceptance does not imply a public CLI release or deployment.
oink is a command-line tool for OINK site maintainers. It brings site creation,
environment diagnosis, output validation, local preview, and theme upgrades
into one interface. Its six commands already form a usable local workflow.
The next useful investment is to help more users install it, understand
failures, and repeat routine maintenance. Migration, documentation version
management, and API reference generation can follow one capability at a time.
This page explains current capabilities and possible next steps. For complete installation instructions, see Using OINK CLI. Executed tests are recorded in the first-stage acceptance report.
As of 2026-09-30, this page describes local 0.1.0-dev, commit e623d93.
Code, tests, installation, and reproducible archives have been prepared and
validated. Public CLI publication and deployment have not been completed.
Future capabilities below are suggestions or existing proposals, not available
commands or committed delivery dates.
Purpose and audience
The OINK theme owns presentation, navigation, search, content components, and output formats. Hugo loads configuration and renders the site. The CLI connects those inputs and results into repeatable maintenance: configuration mistakes, the actual theme source, broken references, and proposed upgrade changes should all have inspectable evidence.
The CLI is a standalone Go executable that calls external Hugo. It requires no Python, Node.js, account, or background service. Initialized sites retain normal Hugo configuration and content. Once dependencies are available, ordinary Hugo can build them without the CLI. Theme and CLI version numbers serve different purposes.
It serves three audiences: new maintainers establishing a working baseline, existing maintainers diagnosing and upgrading sites, and CI or automation programs consuming stable JSON results and exit codes.
The six implemented commands
| Command | Problem it addresses | Current behavior and boundary |
|---|---|---|
oink doctor |
Is the environment ready, and what does this site actually use? | Inspects Hugo Extended/version, required Go/Git, effective configuration, the declared theme version and resolved source, workspaces, replacements, vendor, languages, and outputs. Read-only diagnosis; no site build. |
oink check |
Does the rendered site contain detectable problems? | Copies inputs, isolates output and caches, builds with --panicOnWarning, then checks actual local links, anchors, resources, and supported machine outputs. |
oink init <directory> |
How can I get a usable, reproducible starting point? | Creates a site from an embedded, fixed Starter snapshot with its license and provenance. Supports en, en,zh, and all (EN/ZH/FR); validates before creating files and refuses nonempty targets. |
oink upgrade --to <tag> |
Will an upgrade work, and what will it change? | Handles one selected site, validates a candidate, and reports a plan. Preview is the default; only explicit --write applies selected module-file changes. |
oink dev |
How do I start everyday local preview? | Transparently runs hugo server, forwards arguments after --, and forwards process signals. |
oink build |
How do I run a strict production build? | Transparently runs Hugo, defaults to production, and adds --panicOnWarning. It does not run the additional reference checks provided by check. |
doctor inspects readiness; check also builds and validates artifacts.
build produces the site’s normal publishing output, while check validates
in an isolated copy. dev and build can write normal Hugo output and caches;
read-only diagnosis and upgrade preview preserve site sources.
Checks follow rendered output
check asks Hugo to enumerate the output formats and URLs declared by each
page in each language, then verifies the generated files. It handles multiple
languages, root URLs and subpaths, URL encoding, and external-link boundaries.
It does not derive a second routing system from Markdown filenames. Page-level
output overrides, statically mounted unlisted pages, and intentional link-only
pages follow Hugo’s actual semantics.
Supported machine outputs include NAVJSON v1 navigation trees, BookManifest v1 book manifests, offline search indexes, LLMS indexes, and LLMSFULL content bundles. Disabled outputs are optional. A valid artifact in one language cannot hide a missing required artifact in another. An unknown required contract is reported as incomplete coverage.
check --release verifies a build intended to use a public theme version. It
disables both Go and Hugo workspaces and Hugo replacements in the isolated
copy. A conflicting theme go.mod replace is reported, never silently removed.
Ordinary check can validate a selected vendor build; --release explicitly
reports that verification of its public-source byte identity is incomplete.
Upgrades validate the candidate first
An upgrade plan identifies the target version, selected files, and before/after
state. --expect-plan can bind a write to a reviewed plan; the command also
checks whether target files changed after planning. Dirty target module files
are protected, unrelated dependencies and edits survive, and write failures
provide backup and recovery evidence. Recovery must preserve concurrent user
edits instead of overwriting them to undo the CLI’s own work.
Current upgrades handle go.mod and go.sum. They do not refresh _vendor or
rewrite arbitrary content or configuration. Vendor refresh requires a separate,
explicit, reviewable workflow. The CLI does not commit, push, or deploy.
Automation and offline use
Every command is non-interactive. With --json, stdout contains exactly one
oink.result/v1 result; tool logs go to stderr. Results include rule IDs,
severity, known locations, explanations, actions, coverage, and raw Hugo
evidence. A source line number is never invented when the available evidence
cannot establish it.
| Exit | Meaning | Interpretation for automation |
|---|---|---|
0 |
Required work completed without blocking findings. | This request passed; still inspect coverage that was not checked. |
1 |
Completed checks found policy violations. | Fix the reported inputs and check again. |
2 |
Required work did not complete. | Investigate tools, builds, I/O, caches, or unsupported inputs; this is not a passing check. |
The default process policy is offline. Only explicit --network allows network
use for that invocation. The CLI does not download Go toolchains, install system
packages, change global configuration, or add telemetry. Supported workflows
can run offline after module preparation; a missing cache entry fails explicitly.
Isolated commands use disposable caches. A successful init --network does
not establish a persistent cache for subsequent commands. Only prepared module
download artifacts are reused; Hugo’s global remote-resource cache is not.
Required remote content must be materialized locally or fetched during an
invocation that explicitly permits network access.
A complete working path
After installing locally and installing the Go, Git, and Hugo Extended versions required by Starter, use the following sequence. The first command is an explicit dependency-preparation step that may use the network:
Edit the site title, baseURL, and content during preview. After stopping the
preview process, run:
For an existing site’s upgrade, preview first, then review and explicitly apply the plan using the upgrade guide. OINK v1.1.0 here is the tested initialization baseline, not a claim that it is always the latest theme version.
Verified scope and current limits
First-stage acceptance covers Go tests, vet, race checks, ordinary Hugo builds for all three Starter profiles at root URLs and subpaths, and three real consumer repositories: the OINK documentation site, the PIG site, and repository documentation. It also includes initialization and checking with OS-level network denial, real upgrade write protection, thin-wrapper process checks, and archive reproduction.
The exercised runtime platform is macOS arm64, with Go 1.27.1 and Hugo Extended 0.166.0. Darwin amd64 and Linux amd64/arm64 were cross-compiled but have not completed runtime qualification on those systems. Windows is outside the first-stage support scope. Source installation is available; public downloads, tag-based installation, and Homebrew distribution are not delivered yet.
Isolated checks currently support materialized, single-host sites. Linked Git worktree metadata, mounted symlinks, mounts outside the isolated inputs, custom configuration directories, dynamic content adapters, multihost language output, and render segments that suppress verification probes have explicit boundaries in the input scope table. Unsupported required inputs cannot receive a complete passing result.
Static checking does not certify browser interaction, accessibility, external URL availability, hosting redirects, translation completeness, or content semantics. Browser and deployment acceptance remain separate workflows.
Improvements to prioritize next
First reduce friction in the six existing commands. These are suggested capabilities based on current limitations, not implemented features. New flags and contracts still belong in the formal proposal process.
| Priority area | Possible additions | Evidence of completion |
|---|---|---|
| Installation and platform support | Exercise complete workflows on target macOS/Linux systems, publish checksummed archives, and provide reproducible tag-based or Homebrew installation. | New users can install, initialize, preview, and check using public instructions; supported platforms have execution evidence. |
| More actionable diagnosis | Group findings by tools, dependencies, configuration, and artifacts; add rule explanations and repair examples; map to source only when reliable; evaluate CI annotations or SARIF export. | Users can identify the input to change, CI retains raw evidence, and false positives can be reviewed against real samples. |
| Explicit dependency preparation | Offer deliberate cache preparation and missing-input reports, distinguish modules from remote resources, and record exact versions, sources, and network requirements. | One preparation step supports repeated offline runs, with precise explanations when an input is missing. |
| More complete upgrade maintenance | Evaluate separate vendor candidate refresh and byte comparison, persistable review plans, and clearer recovery instructions. | Users can review the complete diff while vendor content, unrelated dependencies, and concurrent edits remain protected. |
| Easier initialization and authoring | Accept declarative site title, URL, and supported language-profile inputs; add a small set of official document, article, and Book page templates. | Fewer manual placeholder edits; output remains ordinary Markdown, data, and Hugo configuration. |
| Broader real-project coverage | Qualify common structures such as linked worktrees first; extend to multihost, external mounts, and dynamic content when needed; optimize large-site checks from measurements. | Every added input profile has regression evidence for source preservation and explainable failures. |
These directions should not all start at once. Use independent installation and maintenance sessions to identify recurring obstacles, then choose one measurable improvement at a time. Future ignore rules or lint baselines must not hide Hugo build failures or missing required coverage.
Later product capabilities
The following directions are discussed in the CLI roadmap and remain proposals. They preserve the boundary that generated site sources are ordinary files and rendering does not depend on the CLI.
| Capability | The CLI’s possible responsibility | Prerequisites and limits |
|---|---|---|
| Bounded Docsy migration | Produce an assessment classifying inputs as compatible, convertible, requiring review, or unsupported; later convert into a new directory and compare old/new routes. | Start with one documented profile from real sites, not arbitrary Docsy, MDX, or React conversion; preserve original files and literal code examples. |
| Documentation version lifecycle | Prepare version snapshots, maintain a small version manifest, and validate page correspondence and archive status. | The theme owns reader presentation; the CLI generates reviewable configuration. Missing pages must not be presented as equivalents, and versions remain independently buildable. |
| Static OpenAPI reference | Generate operation, parameter, request/response, and schema Markdown/data from local specifications for the existing Hugo output pipeline. | Define the specification subset, generate deterministically, and protect manual edits. Prepare remote references explicitly; request execution, credentials, and SDK platforms are separate work. |
| Agent and editor integration | Evaluate editor entry points or MCP over stable JSON results so other tools can reuse the same diagnostics and upgrade plans. | Establish repeated use of the core commands first. MCP, Studio, graphs, and hosted services require independent demand and maintenance capacity. |
The recommended sequence is a publicly usable maintenance tool, followed by assessment for one migration path. For the next content capability, prefer documentation version lifecycle unless real API users demonstrate stronger repeated demand for static OpenAPI. Choose one foundation per stage to avoid maintaining several models before users have validated them.
Further reading
- Using OINK CLI: builds, installation, flags, and complete workflows.
- CLI and result contract: stable behavior, JSON, exit codes, and file protection.
- First-stage acceptance: executed tests, real sites, and platform boundaries.
- CLI and the next product stage: rationale, priorities, and acceptance conditions for future work.
5 - Use the OINK CLI
oink is an optional Go CLI for Hugo sites. The local 0.1.0-dev candidate
focuses on diagnosis, real output checks, initialization, builds, theme
upgrades, and guarded maintenance plans. Hugo remains the renderer. Sites
continue to work with ordinary Hugo.
This guide describes the reduced command surface on 2026-10-04. The CLI has no established public release or distribution. Earlier R1–R8 acceptance belongs to its historical source and binaries. The CLI contract defines current behavior. Studio, general editing, context, snippets, editor setup, and CI generation are retired.
The current cached-module move integration failed once and passed a targeted rerun; the intermittent failure remains open. For the recorded scope, see the verification limits.
Build and install locally
From an available oink-cli source checkout, use Go 1.26 or newer and Make:
The export affects this shell only. The CLI does not install system tools or
change a shell profile. make install PREFIX=/your/prefix chooses another
prefix; BINDIR=/your/bin selects the exact directory. A source build needs
the dependencies in go.sum; make deps explicitly prepares them when network
access is available. The build and install targets then use the local
toolchain without downloading dependencies or another Go compiler.
The dated runtime acceptance record exercised its identified historical candidate on macOS arm64, native Linux arm64 and Linux amd64 emulated through QEMU TCG, with Go 1.27.1, Hugo Extended 0.166.0 and the public OINK v1.1.0 module. The Hugo version gate accepts Extended 0.160.1 or newer; this is not a claim that every accepted version has been tested. The embedded Starter documents Hugo Extended 0.165.0 or newer and requires Go 1.27. Those Linux tests ran as nonroot users on ext4 with provisioned offline dependencies; required filesystem/signal and selected actual-Hugo cases execute. Optional tools absent from a guest stay explicit skips; they have separate host protocol evidence. Two fresh builds reproduced all five archives, and the three declared runtime archives were extracted and executed outside a checkout without Node. Darwin amd64 is an experimental archive and remains unverified after actual Bad CPU type; cross-compilation does not prove runtime support. Windows is outside the declared scope. These results remain bound to the recorded source and archives; they do not qualify the later reduced CLI or a newly built executable automatically.
make release VERSION=0.1.0-dev DIST=dist prepares four binary archives, a
source archive, and SHA256SUMS in a new or empty directory. It does not
publish them. See the archive acceptance and reproduction steps
for the exercised platform and reproducibility limits.
Create a site from the fixed Starter
Prepare the public theme once if it is not already cached. This explicit provisioning command may use the network:
init accepts a new directory or an existing empty directory. Its parent must
exist. It refuses existing files, including dotfiles, and refuses a symbolic
link as the target. It validates a temporary candidate before creating any
target file and detects target changes during that operation.
Choose --profile project (the default), docs, blog, or book. project
keeps the complete previous Starter projection. The other choices retain their
archived content section and adapt the existing localized site title, home
cards/actions and navigation to that section. Selected content and shared
assets/examples/workflows/license retain their archived bytes; generated
configuration and homepage YAML are the only serialized profile projections.
The archived workflow examples remain unchanged and are not the checksum-bound
CI plans from ci init.
Language choices remain en (default), en,zh, and all (English, Chinese and
French), independently of the selected profile. Every composition uses the
same MIT-licensed Starter commit
137843b25bacd76ddd1f7ce71330bf2e3155b954 embedded in the executable, pins OINK
v1.1.0 with recorded Go checksums and disables enableGitInfo before the first
Git commit. No runtime template fetch, Git initialization or commit occurs.
Unknown profiles fail before writing; unavailable or failed required Hugo
validation leaves the new/empty target unchanged.
Edit the title and baseURL in my-docs/hugo.yaml, then edit the home data and
sample content using the Starter tutorial. After
creating Git history yourself, you can enable enableGitInfo if wanted.
Ordinary Hugo can build the generated site without the CLI:
This uses the GOMODCACHE exported above and provisioned dependencies. All
12 profile/language combinations passed ordinary warning-strict Hugo at both
root and /manual/ URLs (24 builds). Rendered local references were checked,
and complete source bytes/modes/file inventories compared equal before/after.
Public init/check tests separately cover all four profiles in English and
bilingual configurations, default-project byte/mode parity and failure paths.
Create ordinary content
Start from the initialized bilingual site above. Preview a new bundle and its Chinese draft, inspect the diff and candidate result, then apply the saved plan:
--language defaults to the effective default language; --kind defaults to
page and also accepts docs, blog, or book. The site-relative bundle path
must map unambiguously through actual content mounts and their language-site
matrix. Language directories retain distinct physical indexes; shared filenames
use the actual language relationships. Existing bundles or
same-page sibling files are preserved. The primary file is an ordinary page;
selected translation files are drafts with the supplied title as placeholder.
They remain unreviewed until explicit human review. Each proposed file must
be recognized as one actual site-owned Hugo page with actual rendered outputs;
link-only/no-output, ignored or build-never new files cannot pass merely because
existing content builds cleanly.
Saving a plan does not write those site files. Apply rechecks the bound fresh
directory and source state and preserves later editor attachments on failure.
Configure editor hints and snippets with the ordinary site editor. The CLI no longer generates these settings.
Inspect pages and committed impact
Use an actual page ID, Hugo Path, permalink or captured source path. The language:path ID removes multilingual selector ambiguity:
Choose IDs from the actual captured page facts; these example pages must exist
in your site. inspect exposes observed references, actual outputs, translation
peers and physical bundle inputs. impact renders the selected committed Git
tree and the current site, including old deleted identities and unchanged
inbound pages. Changed global configuration/templates/data and uncertain
ownership expand scope. An observed alias output with unproven page ownership
also causes full scope, without guessing an owner from front matter.
check --since currently runs the full current check. Read
data.check_scope: full separately from causal data.impact.full_scope.
Completed inspect/impact queries return 0 while quality findings remain
in data.current_check; full checking retains policy findings 1. Missing
or unrenderable history is required incomplete 2: known current facts remain
visible, while old identities and changes stay unknown. Current external
local dependencies are never borrowed as historical bytes. A committed
site-owned theme is supported; symlink/submodule and required unsupported or
incomplete history states are explicit limits.
Preview and apply a content move
Use clean physical site-relative file/bundle paths and save the new plan
outside the selected site. The preview shows original checks, a provisional
route probe, final validation, translation/attachment mappings, byte/full-mode
diffs, actual old/new routes, alias advice and manual references. A provisional
probe may report stale-link findings 1; only the final candidate can validate
the plan. Raw HTML, shortcode output, transformed or ambiguous destinations
are not rewritten. Repeated ordinary Markdown destinations can also stay
manual when exact source/output occurrence ownership is unproven, including
aggregate/print output. Their final broken targets return 1 and no plan is
saved. Review manual source locations and actual output pointers in your editor,
then create a fresh preview. A moved attachment needs a proven new published
URL, not merely a new physical path. Paired equal-byte processed image outputs
can be proven while absolute original-resource URLs remain manual; those
unproven URLs are not constructed automatically.
Aliases are advice for review, not automatically serialized front matter.
Explicit saved apply captures and regenerates the actual proof before selected
writes. Complete source hashes/modes/inventory, external inputs and fresh
target directories remain guarded. Existing targets, later source/attachment/
configuration edits or mode changes return 2 without overwriting them.
The move preserves original modes and binary bytes, unrelated files and the
Git index; it does not commit. The resulting ordinary Hugo inputs remain
buildable with Hugo independently of the CLI. Inspect the named recovery
directory if an apply reports a failure during writes.
Diagnose and validate an existing site
Run these commands from any directory and select exactly one site:
doctor reports the real Hugo executable and version, required tools, the
declared theme pin, effective Hugo configuration, module graph and mounts,
workspaces, replacements, vendor presence, languages, and enabled outputs.
It does not build the site. Keep the raw subprocess evidence alongside the
structured findings when investigating a Hugo error.
check copies the inputs into a disposable directory, isolates build outputs
and caches, invokes Hugo with --panicOnWarning, and checks supported local
links, anchors, resources, and machine-output references against the rendered
files. Hugo itself enumerates each page’s enabled output formats and URLs for
each language, including front matter overrides and statically authored pages
excluded from ordinary page lists. A temporary verification output is added
only in the disposable copy and removed before artifact checks. The CLI does
not infer routes from Markdown filenames. Disabled machine outputs are not
errors. Coverage entries identify checks that were
completed, omitted, unsupported, or incomplete. Browser interactions,
accessibility, external URL availability, server redirects, and deployment are
outside this static check.
JSON data.pages supplies Hugo’s page identities, actual routes, aliases,
languages, translations, publication settings, known source provenance and
outputs. data.references supplies observed rendered references and checked
anchor state. Generated pages with no proven file retain an explicit unknown
source. These are production-view facts; their presence does not certify
undeclared translation coverage or invent a Markdown source line.
Human output summarizes page/reference counts; use JSON for the full arrays.
Both commands preserve source files. --keep-work retains the disposable
directory and reports its path for inspection; otherwise it is removed. Use
--config FILE, --environment NAME, and --hugo PATH when the site needs a
specific configuration, environment, or Hugo executable. Configuration files
must be inside the selected site. For inspection, --environment takes
precedence over HUGO_ENVIRONMENT; otherwise the environment is production.
If Hugo adds or changes go.mod or go.sum in the temporary copy, the CLI
reports that required dependency preparation remains unreviewed; it does not
apply those changes to the source or silently call the original inputs ready.
--release disables both Go and Hugo workspaces and disables environment and
Hugo-configuration replacements in the disposable copy. It preserves go.mod
replacements. A local
OINK replacement must be reviewed explicitly before claiming a public-pin
check; the CLI does not silently delete it. Vendor evidence is also separate:
a public requirement in go.mod does not establish which bytes are in
_vendor.
The initial isolated-check scope has these limits:
| Input shape | Current behavior |
|---|---|
Normal checkout with its own .git directory, or materialized files without Git |
Supported within the other documented boundaries |
Linked Git worktree with a .git file |
Rejected; use a separate materialized copy with its own Git metadata if Git history is needed |
| Mounted symlinks or mounts still pointing outside the isolated snapshot | Rejected; materialize those inputs inside the selected site or supported local dependency |
| Unmounted auxiliary symlinks | Omitted from the snapshot; this does not validate their contents |
Mounts using excluded public, resources, node_modules, or tmp trees |
Rejected when they are required source inputs; keep authored/generated source in a dedicated source directory |
Custom HUGO_CONFIGDIR outside the supported config location |
Rejected; use the site’s config tree or an explicit in-site --config file |
Hugo content adapters (_content.gotmpl) |
Complete enabled-output enumeration is unsupported; check and candidate validation return incomplete work |
| Multihost language configuration | Unsupported for full output validation; returns incomplete work rather than treating each host as one output tree |
Likewise, disabling page rendering or selecting render segments that omit an
enabled language’s verification output cannot produce a successful full
check. These are coverage limits, not instructions to delete a worktree,
replacement, symlink, or authored content. doctor can still inspect supported
configuration without claiming a completed output build.
Select checks and record project policy
Create a regular oink.yaml at the site’s root when the project needs explicit
checking policy. It uses schema_version: oink.policy/v1 in one YAML document.
Keep languages, menus, URLs and theme versions in existing Hugo/module inputs.
Unknown policy keys/groups, invalid reviews and required disabled groups
return 2.
This example retains required links and demonstrates a reviewed finding and a separately deployed URL scope. Replace illustrative paths and review metadata with actual project decisions:
Rules use exact diagnostic IDs and error, warning or info. Exclusion
globs use clean relative paths; recursive ** and escape paths are unsupported.
Excluded findings stay visible with disposition: "excluded" and review
metadata. Checks never create review records as a side effect. Required
build/input/tool failures and unsupported coverage remain 2 despite policy.
For a site at https://example.org/manual/, same-origin HTML /status/
references normally fail as outside the published base path. The reviewed
scope declares that separate application; availability remains untested.
Complete path-segment matching excludes /status-other/. A scope cannot hide
missing targets inside /manual/ or required local machine-output references.
Without a policy, check enables required links, translations and style.
check links, check translations and check style each select one required
engine; unselected groups report optional not_checked. Explicit policy groups
can disable optional checks. Every check retains its strict Hugo prerequisites.
Declare translation coverage
Start with Hugo’s actual page identities in check --json (data.pages), then
declare the source Page.Path scope and required enabled languages. Paths are
Hugo source identities, independent of slug, URL, aliases or language prefixes.
Extend the same single oink.yaml object; this complete example also declares
protected prose and the baseline path:
Use real page paths, filenames, IDs and protected literals from your project.
mode defaults to localized, drafts to include. Strict mode with
explicit_ids: true requires complete recognized explicit-ID correspondence;
localized mode protects the selected ids. Placeholder counts, named fenced
code, required dotted fields and equal dotted values are separate opt-in
constraints. Other prose, heading counts and code may differ.
drafts: ignore skips draft sources and treats draft targets as unavailable;
require-published requires the source and required targets in the production
view. Hugo-disabled known languages are optional not_applicable; unknown
languages fail policy loading. Without scopes, existing default-language pairs
and duplicate relationships are inspected, but universal localization is not
required. JSON data.translations distinguishes missing, draft and hash-review
states. An explicit nonpublishable analysis includes draft/future/expired pages
without replacing production output or publishing them.
Inspect source rules and provenance
Generic rules inspect recognized explicit IDs and declared protected prose.
The parser follows effective Hugo markup.goldmark.parser.attribute.title
and .block, plus markup.goldmark.extensions.passthrough.enable and its
configured .delimiters. These settings remain in Hugo configuration.
It keeps original UTF-8/CRLF/BOM offsets and accepts
unknown valid YAML/TOML/JSON front matter. Code, shortcode bodies, raw HTML and
math contents are excluded from prose evidence; an attribute after a fence is
not treated as a supported code attribute. Unsupported required source syntax
or absent declared protected inputs returns 2.
The small native OINK v1.1.0 catalog reports advisory code/table conflicts,
deprecated attribution fields and attributes the published theme drops.
data.native_rule_provenance records immutable source/license hashes. The
catalog runs only for a SHA-verified actual public module-cache mount. Other
versions, replacements, vendor copies and unknown identities report optional
native-theme-rules: not_checked; generic rules still run. Review this coverage
before treating the check as complete for a particular component.
Review translations and apply metadata plans
Use the exact Hugo IDs or unambiguous captured source filenames from the report:
Review previews .oink/translations.json (oink.translations/v1) after candidate
validation. It never writes the site during preview. The record binds full
source/translation byte SHA-256 values and explicit reviewer/reason/time.
--reviewed-at RFC3339 is optional and defaults to current UTC. No record means
unknown; current, source_changed, translation_changed and both_changed
describe hashes since review, without judging translation accuracy. Modification
time is not review evidence. diff shows captured source text for comparison.
To acknowledge completed, reviewed existing findings while keeping them visible:
The default baseline is .oink/baseline.json (oink.baseline/v1); baseline in
policy can select another clean relative file. Acknowledged exact rule,
normalized location/pointer and condition remain visible with
disposition: "baseline" and review metadata. Severity changes do not invalidate
that fingerprint; new conditions still block. Required incomplete work cannot
be captured or hidden by a baseline.
Both preview commands require reviewer and reason. --plan FILE creates a new
oink.plan/v1 file without overwriting; omitting it prints the validated plan
only. Review the readable diff, site, file list and base byte/mode guards before
plans apply. That command validates a fresh isolated candidate and refuses
stale guards, escapes, .git, symlinks and nonregular files. It writes only
the plan’s selected files; these commands do not use --write. Failed partial
writes restore owned unchanged files and preserve later editor bytes, modes or
deletions. The reported recovery directory retains original/concurrent evidence.
Preview and apply one theme upgrade
Choose an explicit release tag. The following command validates a candidate and prints the proposed module-file changes without applying them:
The normal human output shows a unified module diff, mode changes and bounded
route/alias/capability changes. In JSON, review data.plan_id, data.changes,
data.comparison, baseline/candidate check summaries and raw evidence. A
missing old URL/output blocks the update unless an observed redirect at its
old output file proves preservation; unknown custom alias identity stays
incomplete. This is not universal theme or browser compatibility. To apply
the freshly revalidated reviewed plan, use its recorded ID:
The CLI changes only the selected go.mod and go.sum bytes after candidate
validation. It preserves unrelated requirements, replacement directives,
comments, and unrelated uncommitted work. --write refuses uncommitted changes
to either selected file, and detects changes after planning. Recovery evidence
identifies backups and any rollback that could not safely restore a
concurrently changed file.
The ID binds current copied source bytes/modes/inventory and actual comparison, not just module-file text. A later source/workspace/dependency edit requires a new preview; only selected module files are applied. Unknown resolved pins or changed/unknown renderer/environment cannot pass. Comparison supports a single HTTP(S) base origin/path; multihost inputs stay incomplete. Emitted byte hashes also bind the ID, so nondeterministic templates can require a refreshed preview. No configuration migration is performed automatically; review unsupported changes manually.
An OINK go.mod replace is a blocking condition for this public-pin workflow.
A site containing _vendor is also refused: this release does not refresh
vendor content. Prepare a separate reviewed copy, update the intended pin and
run hugo mod vendor explicitly, then review and validate the entire vendor
change. A go.mod bump alone is never reported as a vendor upgrade.
Preview and build through Hugo
Arguments after -- go directly to Hugo. The CLI shows the effective command
and forwards process cancellation. dev runs hugo server; build selects
the production environment by default and adds --panicOnWarning. These are
direct Hugo operations by default and may create the site’s normal output and
cache files. They do not run the reference checks performed by oink check.
Check and export one build
Choose a real publication URL in OINK_PUBLIC_BASE_URL, prepare the site’s
exact dependencies, then use a fresh output directory and separate new manifest:
Add --network to this operation only if its dependencies or required remote
resources need downloading. An example/local publication address is a release
error; ordinary diagnosis reports a warning. --release also checks actual
public theme resolution independently of local Git history or declared pins.
Hugo renders one isolated production output. The CLI checks, seals and exports
that same tree without rebuilding or changing site sources. Required incomplete
coverage returns 2; blocking findings return 1. Neither result creates a verified
export. An explicit scoped translation policy needing publication-excluded Hugo
identities returns 2; run standalone check/translations for the full
nonpublishable maintenance view, or deliberately select a production policy.
The command does not infer identities from filenames.
The destination must be new or empty and its parent must exist. The manifest
must be a new file outside the public tree. Existing entries are preserved;
failed partial export remains explicitly unverified. Optional --marker adds
only the artifact identity at .well-known/oink-build.json; without the flag,
no marker is added. The local oink.artifact/v1 manifest records original
input identity, known Git state, effective settings/theme/tools, required
coverage, Hugo routes and exact file digests/modes. It excludes absolute local
paths and logs and is saved with mode 0600. Keep it outside the upload.
Managed builds allow only --minify, --gc, --ignoreCache and --noTimes
after --, with optional boolean =true/=false. The ordinary build example
above still accepts transparent Hugo arguments.
Verify artifacts and a deployed site
Immediately before uploading, check the exported directory offline:
This compares the exact files, bytes and full modes. Missing, additional or
modified files invalidate the previous identity. Upload this directory without
rebuilding; preserve its hidden .well-known marker when enabled.
After a separately authorized deployment, check its public URL explicitly:
Verification reads every declared file and distinct actual Hugo route, including
language/subpath URLs, and compares bounded decoded response digests plus recorded HTML
canonical/language identities and the enabled marker. HTTP cannot inspect local
file modes. Wrong content, a soft-404 or a different captured identity returns
1. Timeouts, authentication/rate-limit failures, server unavailability and a
missing required marker return 2; redirects outside the selected origin/path
are blocked. No credentials are discovered or sent. A build’s --network
permission does not authorize this later request or any upload.
Retired CI generation
ci init is removed. Keep CI configuration in the site or Starter.
Local CLI validation does not execute hosted CI or deploy a site. Previously
saved CI plans are rejected by plans apply.
Check explicitly registered sites
Workspace and adapter examples passed owning/runtime, actual protocol, four-consumer parity/preservation and canonical source/render gates. A07/A15 supported scope is accepted locally in the R6 record. They do not describe a published CLI release or a completed platform refresh.
Create a separate registry such as oink.workspace.yaml beside your selected
projects. Its only site fields are name and directory; keep Hugo settings
in each site and checking policy in that site’s oink.yaml.
| Command | Selection |
|---|---|
workspace list --workspace FILE |
List explicit entries without running Hugo |
workspace check [GROUP] --workspace FILE [--sites NAME,NAME] |
Check all or an exact subset, in registry order |
check ... --workspace FILE --site NAME |
Run the normal single-site check for one registered name |
plans apply FILE --workspace FILE --site NAME |
Revalidate and apply only a plan bound to that named canonical site |
Names are case sensitive ASCII identifiers matching
[A-Za-z][A-Za-z0-9_-]{0,63}. A regular nonsymlink registry has one strict YAML
document, 1–64 nonoverlapping sites and a 256 KiB limit. Directories are literal
relative paths from its actual parent, or absolute paths; no environment/glob
expansion or sibling discovery occurs. Canonical aliases identify the same site
and cannot register it twice. Missing directories remain listed; their check
returns 2, while the remaining explicit sites are still checked. The aggregate
returns 2 before 1 before 0, preserving full per-site findings and coverage.
Omitting --sites means all registered sites; an explicit list rejects blanks,
duplicates and unknown names and keeps registry order.
A direct command needs --site NAME; there is no default registry site. init,
artifacts and verify do not accept registry selection. Save a
reviewed plan outside its site, then explicitly apply it to the same name:
Use actual page identities returned by your site’s checks for the review
selectors. Selecting blog for a plan bound to docs returns 2 before a
source write. Preview, validation, freshness and byte/mode safeguards are the
same as direct single-site use; no other registered or neighboring site is
updated automatically.
Configure already provisioned optional tools
The CLI does not install markdownlint, Vale or lychee. After provisioning tools
separately, add explicit entries to the selected site’s oink.yaml. The current
protocols are markdownlint-cli 0.49.1, Vale 3.24.0 and lychee 0.24.2;
other reported versions remain unsupported until qualified.
enabled defaults to true, required to false, and command to the kind’s
name. You can select one provisioned executable by name or absolute path;
commands are not shell snippets. Configuration paths must be clean relative
paths inside the captured site. Process time defaults to 60 seconds, with
bounded nondefault values 1–300. Unavailable optional tools show omissions;
required unavailability or unsupported protocol returns 2. A problem baseline
or lower rule severity cannot turn required incompletion into success.
Markdownlint and Vale belong to style; lychee belongs to links. Select the
group whose tools you intend to run:
The second command explicitly permits actual external HTTP requests. Without
--network, lychee is not invoked: optional coverage is not_checked, required
coverage returns 2. A successful native local-links check does not attest
external availability. HTTP 401, 403, 408, 425, 429, 5xx, DNS/TLS
failures and timeouts are inconclusive, not definite broken links. Other failed
4xx responses are typed findings. External locations remain actual output
files and DOM pointers; the CLI does not guess their Markdown line.
For markdownlint, use declarative JSON, YAML or TOML, for example:
JS/JSONC configs, custom rules and extends are unsupported. The CLI stages
a private rule object behind an unpredictable JSON pointer so upstream rc data
does not change its effective rules. Vale requires an explicit INI and captured
styles. A supported minimal configuration is:
Provide declarative rule files under styles/Project/. Supported rule kinds are
existence, substitution, repetition, occurrence, consistency,
capitalization and sequence. Actions, scripts, packages, sync, conversion
assets and style pipelines require manual review and are not executed by this
adapter. Lychee accepts these bounded request settings:
Allowed ranges are 1–300 seconds, 0–3 retries and 1–32 concurrent requests.
The literal cache = false is also accepted; cache = true is refused. Cache
and preprocessors are disabled; arbitrary extra tool arguments are not
accepted. No adapter fixes or formats source files. Code prose is outside
source attribution; Markdown structure and fence/inline code boundaries remain
available to markdownlint, while Vale receives the prose-only mask. Private
masks preserve proven UTF-8/BOM/CRLF boundaries around front matter, shortcodes,
raw HTML, configured math and attributes; findings from excluded/synthetic text
remain omissions. Only already captured site-owned Markdown is read for prose
tools.
Inspect data.adapters, adapter.KIND coverage and raw evidence before
interpreting an exit. Each adapter retains version/executable/configuration
hashes and protocol provenance. Caller proxy URLs/credentials and Node preload
settings are not forwarded; literal NO_PROXY/no_proxy host-list data may be
retained for the qualified runtime. This does not disable every operating-system
proxy route or create a network sandbox. Network checks do not verify external
fragments, browser behavior or remote content identity.
Retired local Studio
studio is removed from the CLI. Use an ordinary editor and oink dev for
a site preview. Read maintenance facts through inspect and structured reports.
The dated R7 acceptance remains historical evidence for its identified inputs.
Retired Studio views
Read page and quality facts with inspect, check, and structured reports.
Retired browser targets
The CLI Studio browser test targets are removed with the implementation. Current CLI checks remain Go and actual Hugo tests.
Retired general editing
edit and Studio editing are removed. Edit source with an ordinary editor,
then run check. new, move, review records, and baseline plans retain
candidate validation and byte/mode guards. Previously saved editing plans
are rejected. The dated R8 record remains historical evidence.
Retired edit commands
The edit text|field|snippet|attachment command family is removed.
Run new --help or move --help for the retained bounded file workflows.
Retired Studio editing
The CLI does not serve an editor or accept browser Apply requests.
Retained plan review
Retained previews display the complete proposed diff. Save a new plan, then
explicitly run plans apply FILE --site DIR. Candidate validation, source and
external input guards, and concurrent-edit recovery remain required.
Network and offline operation
Network access is disabled by default; --offline makes that choice explicit.
Missing dependencies produce an incomplete result. The CLI does not install
Hugo, download a Go toolchain, change global configuration, or enable telemetry.
Allow the current operation to use the network only when intended:
--network and --offline cannot be combined. Diagnostic and validation
operations use disposable caches; a download there does not establish a
persistent cache for the next offline run. For repeatable offline work,
provision the exact module versions in a normal Go module cache and set
GOMODCACHE explicitly as shown above. Include all transitive dependencies
needed by your site. Isolated validation reuses provisioned module download
artifacts, not Hugo’s global remote-resource (GetRemote) cache. A prewarmed
remote-resource cache alone does not make this check work offline. Materialize
required remote content as local site resources, or explicitly use
--network for that build. Theme assets that already ship as local files do
not require such a download.
Text, JSON, YAML, and automation
| Option | Output |
|---|---|
| Default | Concise colored English text |
--json, -J |
One JSON oink.result/v1 object |
--yaml, -Y |
One YAML document with the same result fields and types |
--verbose, -v |
All findings, coverage details, and tool logs |
--no-color |
Plain English text |
Choose one structured format. Nonempty NO_COLOR or TERM=dumb also disables
text colors. Structured output adds no terminal colors. Tool logs go to
stderr. --format json|yaml and --non-interactive remain hidden compatibility
options. Every command is non-interactive.
Default text shows status, counts, up to eight active findings, and explicit coverage omissions. Detailed facts and reviewed findings remain available in structured results. Plan and upgrade previews retain their complete diffs. Cobra owns command dispatch and focused help. CLI messages use short, active English sentences inspired by ASD-STE100; this does not assert certification. User content and external tool evidence retain their original language.
| Exit code | Meaning |
|---|---|
0 |
Requested work completed without blocking findings |
1 |
Completed checks found a policy problem |
2 |
Required work could not complete, including tool, build, or I/O failure |
Always inspect coverage alongside the exit code. An exit code of zero from
doctor does not prove a build, and a successful static check does not prove
browser behavior or a public deployment. These commands do not commit, push,
publish a theme, or deploy a site.