⚡ Golden path — one note → live wiki

Quartz turns Markdown into a website. Below: one new note in the existing docs wiki. Independent site? Skip to 🌱 Create/start.

Reference — approved publisher only; not run against this vault. Paste once into Bash. Authoring was tested in a disposable directory; an existing filename aborts safely.

(
set -eu
cd /home/loca/dev/wikis
umask 022
# CREATE + AUTHOR: a new file; no overwrite.
( set -C; cat > quartz/content/my-first-note.md <<'MD'
---
title: My First Note
description: My first Quartz page.
tags: [notes]
---
Hello, Quartz!
MD
)
# LINT -> CHECK -> PUBLISH: stop immediately on a failure.
python3 /home/loca/dev/quartz-sandbox/evidence/2026-09-15-quartz-migration-f1fd35c3/wiki-lint.py /home/loca/dev/wikis/quartz/content
./build.sh quartz --check-only
before=$(readlink quartz/current)
./build.sh quartz
after=$(readlink quartz/current)
test "$before" != "$after"
printf 'Published: %s -> %s\n' "$before" "$after"
/usr/bin/curl --silent --show-error --fail --output /dev/null --write-out '%{http_code}\n' https://wiki.quartz.loca.zone/
)

Success: lint 0 → check-only success → changed current → HTTP 200. Open /my-first-note: expect My First Note + Hello, Quartz! Finish 🔎 Verify below. Existing filename? Deliberately edit it or choose another name.

🧰 Prerequisites — use what is installed

ItemReal host fact / success
Node requirementQuartz 5.0.0 requires Node 22+; npm 10.9.2+.
Interactive PATH NodeRecorded engine-vendored v22.16.0: /home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin/node. A supervised process may instead inherit system PATH.
Explicit system tools/usr/bin/node v22.23.1; /usr/bin/npm 10.9.8. Sandbox wrapper pins PATH=/usr/bin:/bin.
Runtime ruleNever upgrade Node or Bun here. Never “fix” a plugin by replacing host runtimes.
Working materialEditor + .md files. Git/npm only for an authorized independent checkout; no privileged authoring commands.

Read-only version commands, executed while preparing this guide:

node --version
command -v node
/usr/bin/node --version
/usr/bin/npm --version

Success: Node ≥22; check its resolved path. The running wiki/sandbox needs no installation.

🌱 Create/start — choose the right lane

GoalWork hereSuccess
Add docs now/home/loca/dev/wikis/quartz/content/One Markdown file becomes one canonical page; index.md is home.
Try sandbox featuresIsolated sandbox vault; workflow belowquartz.loca.zone changes, not the docs host.
Create your own siteA new, authorized empty workspace, preferably your own machineSeparate checkout, dependencies, config, content and output.

Reference — new independent checkout only; not run here. Commands below are not production-engine instructions. Upstream may move: this clone does not reproduce the sandbox pin.

git clone https://github.com/jackyzha0/quartz.git
cd quartz
npm i
npx quartz create
npm run install-plugins
npx quartz build --serve
  • Create prompts: choose default + new; enter your own domain without https://; choose shortest links. Do not choose copy/symlink against someone else’s vault.
  • Success: content/index.md and quartz.config.yaml exist; preview opens at http://localhost:8080; edits reload. Stop preview with Ctrl+C.
  • Lookup: npx quartz buildpublic/; --directory / -d → content (default content), not engine isolation; --output / -o → output. The pinned package has no npm build task.
  • Read more: installation · cli. Host rules here override generic upstream upgrade/deploy advice.

✍️ Authoring — filenames, metadata, visibility

Create kebab-case.md; use real targets. Existing upstream filename spaces become route hyphens. Success: expected title, navigable links, real content.

---
title: My First Note
description: A useful one-sentence summary.
tags: [notes, quartz]
aliases: [first-note]
draft: false
date: 2026-09-16
---
FieldWhat uses it / beginner rule
titlenote-properties parses it; article/metadata uses it. Supply a meaningful title.
descriptionSummary metadata via description (otherwise generated); also displayed by house properties view.
tagsYAML list → tag pages, content index, navigation and properties view.
aliasesalias-redirects emits alternative URLs; link resolver recognizes them. No /; no collision with a real page. Also shown in properties view.
draftEnabled remove-draft excludes draft: true Markdown pages. Boolean, not a secrecy promise.
publishexplicit-publish is disabled here: publish: false does not hide a page. When enabled elsewhere, requires publish: true; draft filtering still wins.
dateParsed date can supply created/published and missing modified metadata. created-modified-date priority: frontmatter → Git → filesystem; house display: modified. Set explicit ISO created, modified, published when they differ.
  • Metadata: YAML between ---; house requires title + description, recommends useful tags.
  • Ignored: private, templates, .obsidian. Exclusion does not protect all attachments or public Git history.
  • Read more: authoring-content · Frontmatter · private pages.

🧩 Syntax mini-reference — copy, then check the result

[[getting-started/installation]]
[[getting-started/installation|Install Quartz]]
[[getting-started/installation#1. Get Quartz]]
![[features/callouts]]
![[getting-started/installation#1. Get Quartz]]
 
> [!tip] Start small
> 
> One page, one useful link, then verify the rendered page.
 
Inline math: $E = mc^2$
Display math:
$$
\sum_{n=1}^{3} n = 6
$$
ConstructSuccess / trap
Wikilink / alias / headingClickable destination, custom label, or heading jump. Shortest resolution works; full paths avoid ambiguity. Wikilinks
! before a wikilinkEmbeds the target page or heading, not just a link. Block embeds add #^block-id to a real target that defines that ID.
Table-cell aliasEscape the alias pipe as shown below; no slash in the label. Inline-code examples inside tables can themselves lose this escape.
CalloutStyled box; > [!tip]- Title starts collapsed. Keep > on every nested line. Callouts
LaTeXTypeset formula via configured KaTeX. Dollar delimiters must balance. Math
Raw HTMLBlank line before opening and after closing block tags such as div or details, or following Markdown may be swallowed.

Copy this source table; the backslash is intentional:

| Guide |
| --- |
| [[getting-started/installation\|Install]] |

Write a fenced block with language mermaid, then paste this diagram body:

flowchart LR
  Write --> Lint --> Check --> Publish --> Verify

Success: a visible diagram, not only an empty Mermaid container. Rendering is client-side: use a real browser and allow 15 seconds. Mermaid diagrams

⚙️ Configuration + plugins — configuration is code

Read: configuration · layout · layout-components. Edit only your authorized site’s config. Preserve all of configuration.theme. baseUrl: domain/path, no scheme.

Reference fragment — merge into a complete sandbox-style config, not a replacement file:

plugins:
  - source: "@quartz-community/search"
    enabled: true
    options: {}
    order: 100
    layout:
      position: left
      priority: 20
      group: toolbar
      display: all
layout:
  groups:
    toolbar:
      direction: row
      priority: 35
      gap: 0.5rem
Key / sourceMeaning / success
sourcePlugin package/repository/local directory; must actually exist. Pinned sandbox uses npm strings such as "@quartz-community/search".
github:quartz-community/searchGit-backed source form used by the older shared engine’s house config; do not mechanically convert it.
./plugins/exampleRelative to the engine working directory; local package, not npm. Actual sandbox locals use ../plugins/sandbox-reading-stats and ../plugins/sandbox-manifest.
enabledBoolean: load/use this entry. Disable is not uninstall.
optionsThat plugin’s own options; consult its docs. The npm font package is @quartz-community/quartz-fonts, not fonts.
orderProcessing order within plugin categories; smaller first. Not visual layout order.
layout.positionPlace a component in left, right, beforeBody, or afterBody; do not invent a slot.
layout.priorityVisual order within a position/group; smaller first. Unrelated to date-source options.priority.
layout.groupNamed group defined under top-level layout.groups; related controls share a row/column.
layout.conditionNamed predicate such as not-index, has-tags, has-backlinks, has-toc; not inline JavaScript.
layout.displayall, mobile-only, desktop-only. Verify at the corresponding viewport.

Pinned sandbox: npm run install-plugins regenerates the index; wrapper runs it before building. It does not replace an authorized install of missing npm dependencies or upgrade Node.

🧱 Enabled house roster — grouped by job

JobEnabled plugin names
Parse + transformnote-properties, created-modified-date, description, obsidian-flavored-markdown, github-flavored-markdown, syntax-highlighting, latex, crawl-links (shortest)
Read + navigatepage-title, article-title, content-meta, explorer, graph, search, backlinks, breadcrumbs, table-of-contents, darkmode, reader-mode, spacer, footer
Emit + stylecontent-page, folder-page, tag-page, canvas-page, bases-page, alias-redirects, content-index (sitemap + RSS), og-image, favicon, fonts, cname
Visibilityremove-draft, unlisted-pages, encrypted-pages

Success: intended plugin effect appears. Disabled here: explicit-publish, comments, citations, recent-notes, stacked-pages. Sandbox locals are separate.

🔎 Verify + publish — green build ≠ good page

Reference — approved publisher only, after the lint command in the golden path:

cd /home/loca/dev/wikis && umask 022 && ./build.sh quartz --check-only
cd /home/loca/dev/wikis && umask 022 && ./build.sh quartz
readlink /home/loca/dev/wikis/quartz/current
  • Check-only: temporary output + index.html guard; deletes temporary output, never moves current.
  • Lock: exclusive flock on /home/loca/dev/wikis/.global-build.lock; transactional config/style substitution + restoration. No bypass or concurrent wiki builds. Run this Quartz-specific lint separately first.
  • Publish: validated output → atomic current swap. Compare readlink before/after; never manually edit dist/current.
  • Permissions: umask 022; files must be readable, directories traversable. Never publish with umask 077 or restrict a served dist.

Read-only commands, executed against the current root; each run gets a fresh Chrome profile:

/usr/bin/curl --silent --show-error --fail --output /dev/null --write-out '%{http_code}\n' https://wiki.quartz.loca.zone/
/usr/bin/google-chrome --headless --no-sandbox --disable-gpu --user-data-dir="$(mktemp -d /tmp/cheatsheet-chrome.XXXXXX)" --virtual-time-budget=15000 --dump-dom https://wiki.quartz.loca.zone/ > /tmp/cheatsheet-wiki-dom-f1fd35c3.html
Live assertionRequired result
HTTP + rendered <title>Root 200; browser title Welcome to Quartz 5 — Quartz Docs. For your note: My First Note — Quartz Docs. No redirect-stub substitute.
Literal [[Zero outside <pre> / <code> subtrees. This syntax-reference page intentionally has examples inside code: count those separately. Current root has zero in the entire DOM.
Search UIA button.search-button and input.search-bar exist. Presence is not proof that a search interaction works.
Your changeOpen its canonical URL; expected title/body, working links and visible diagram/math where used. Inspect the browser, not only source files or HTTP status.

Success: all applicable assertions pass. Earlier root proof does not accept this new page; publisher must render its final route.

🧪 Sandbox workflow — isolated experiments, hot releases

Start at index; see live sandbox, config, plugins, and route manifest.

PartExact boundary / success
Engine/home/loca/dev/quartz-sandbox/sandbox/engine; 5.0.0, commit 3dff48b5df6d84c9544a5ae19c8f2cbb01dc44e5. Own dependencies/config/style.
Author/home/loca/dev/quartz-sandbox/sandbox/vault; authorized experiments only.
Wrapper/home/loca/dev/quartz-sandbox/sandbox/build.sh: 0–1 positional vault directory, no check-only flag. Default sandbox/vault; missing path falls back to isolated engine/docs — check spelling.
GuardsSystem PATH + umask 022; pinned commit; project-contained engine realpath; nonblocking .build.lock; rejects existing release path or absent index.html.
Sequence (reference from script)/usr/bin/npm run install-plugins/usr/bin/node ./quartz/bootstrap-cli.mjs build --directory "$vault" --output "$dist" → atomic swap. Keeps new + two prior timestamped releases and dist-baseline.
QUARTZ_SANDBOX_ENGINEOptional engine path; realpath must remain under /home/loca/dev/quartz-sandbox/. Never the shared engine.
SANDBOX_ALLOW_DRIFTDefault 0; exactly 1 permits commit mismatch with warning. Explicit expert authorization only; never bypasses isolation.
serve.mjs/home/loca/dev/quartz-sandbox/sandbox/serve.mjs; SANDBOX_ROOT defaults to sandbox/current, confined inside sandbox. PORT: 51947 default; integer 1–65535.
RuntimeAlways 127.0.0.1. Unit: /usr/bin/node, PORT=51947, SANDBOX_ROOT=…/sandbox/current, NODE_ENV=production (server does not read this last variable).
Hot swapServer notices current changes on the next request: no service restart. HTML is no-cache; only GET/HEAD are allowed; traversal is rejected.

Reference, not run here: /home/loca/dev/quartz-sandbox/sandbox/build.sh. No second server on the occupied port. Success: new release notice + changed live content, no restart.

  • Recorded showcase: 47 HTML / 143 files; manifest 44 entries. These counts describe the recorded release, not permanent quotas.
  • Local-only: sandbox-reading-stats uses wordsPerMinute: 220, locale: en; sandbox-manifest defaults fileName: sandbox-manifest.json, pretty: true. Both receipts say safe_for_shared_engine: false.
  • Privacy: the manifest includes unlisted published content; unlisted is not private. Encryption features are not permission to commit secrets.

🛡️ Safety — hard stops

  • 51-wiki fence: /home/loca/dev/quartz/quartz is the shared engine. Never manually edit, build, install into, switch branches in, or “modernize” it. Only the authorized house wrapper may perform its locked temporary config/style transaction.
  • One wiki build at a time. No direct shared-engine CLI, no bypassing locks, no parallel publish/check-only jobs.
  • Lint before publish; umask 022; no hand-edited dist/current. Stop on errors instead of applying broad permission changes blindly.
  • Keep secrets out of the vault and repository. Drafts, unlisted pages, encrypted-page metadata and excluded paths are not a general secret store.
  • No Node/Bun upgrades. No service restart for a sandbox content release. A sandbox “safe” experiment verdict is not approval to change the shared engine.

🧯 Troubleshooting — symptom → cause → next move

SymptomReal cause / fix → success
403 root / 404 pages after green buildumask 077 can poison dist permissions. Publisher inspects readability/ownership, rebuilds with umask 022; no blind blanket-chmod → 200.
Literal wikilinks outside code; following headings swallowedMissing blank line around raw-HTML blocks. Add the boundary blank lines, lint, republish → real links/headings.
Page missingdraft: true with remove-draft, or matching ignorePatterns. Correct intended visibility/path → canonical route exists. publish: true does not override draft filtering.
Cannot read properties of undefined (reading 'fontOrigin')Missing configuration.theme. Restore a complete site’s theme block, not a plugins-only config → check build proceeds.
Table wikilink split across cellsUnescaped alias pipe. Use | inside the table’s wikilink → one intact cell and link.
Alias label truncated/ inside the alias label. Replace with a slash-free label → full rendered text.
Mermaid blank despite HTTP 200Client rendering not finished/proved. Use real Chrome with --virtual-time-budget=15000; inspect visible SVG/diagram, not just container count.
Pinned-sandbox plugin change absentForgot npm run install-plugins, wrong source/options, or disabled entry. Use the wrapper’s install-before-build path and check logs → intended output effect.
Sandbox lock refusal / commit mismatchAnother rebuild holds .build.lock, or engine drift. Wait for owner or restore approved pin; never delete locks or casually enable SANDBOX_ALLOW_DRIFT → guard passes legitimately.

Grounding: actual scripts/config/plugins, live root Chrome captures, and the three sandbox-*.json receipts in /home/loca/dev/quartz-sandbox/evidence/2026-09-15-quartz-migration-f1fd35c3/. Runtime receipt records an earlier manual baseline, not today’s service state.