⚡ 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
| Item | Real host fact / success |
|---|---|
| Node requirement | Quartz 5.0.0 requires Node 22+; npm 10.9.2+. |
| Interactive PATH Node | Recorded 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 rule | Never upgrade Node or Bun here. Never “fix” a plugin by replacing host runtimes. |
| Working material | Editor + .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 --versionSuccess: Node ≥22; check its resolved path. The running wiki/sandbox needs no installation.
🌱 Create/start — choose the right lane
| Goal | Work here | Success |
|---|---|---|
| Add docs now | /home/loca/dev/wikis/quartz/content/ | One Markdown file becomes one canonical page; index.md is home. |
| Try sandbox features | Isolated sandbox vault; workflow below | quartz.loca.zone changes, not the docs host. |
| Create your own site | A new, authorized empty workspace, preferably your own machine | Separate 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 withouthttps://; chooseshortestlinks. Do not choose copy/symlink against someone else’s vault. - Success:
content/index.mdandquartz.config.yamlexist; preview opens athttp://localhost:8080; edits reload. Stop preview with Ctrl+C. - Lookup:
npx quartz build→public/;--directory/-d→ content (defaultcontent), 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
---| Field | What uses it / beginner rule |
|---|---|
title | note-properties parses it; article/metadata uses it. Supply a meaningful title. |
description | Summary metadata via description (otherwise generated); also displayed by house properties view. |
tags | YAML list → tag pages, content index, navigation and properties view. |
aliases | alias-redirects emits alternative URLs; link resolver recognizes them. No /; no collision with a real page. Also shown in properties view. |
draft | Enabled remove-draft excludes draft: true Markdown pages. Boolean, not a secrecy promise. |
publish | explicit-publish is disabled here: publish: false does not hide a page. When enabled elsewhere, requires publish: true; draft filtering still wins. |
date | Parsed 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 requirestitle+description, recommends usefultags. - 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
$$| Construct | Success / trap |
|---|---|
| Wikilink / alias / heading | Clickable destination, custom label, or heading jump. Shortest resolution works; full paths avoid ambiguity. Wikilinks |
! before a wikilink | Embeds the target page or heading, not just a link. Block embeds add #^block-id to a real target that defines that ID. |
| Table-cell alias | Escape the alias pipe as shown below; no slash in the label. Inline-code examples inside tables can themselves lose this escape. |
| Callout | Styled box; > [!tip]- Title starts collapsed. Keep > on every nested line. Callouts |
| LaTeX | Typeset formula via configured KaTeX. Dollar delimiters must balance. Math |
| Raw HTML | Blank 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 / source | Meaning / success |
|---|---|
source | Plugin package/repository/local directory; must actually exist. Pinned sandbox uses npm strings such as "@quartz-community/search". |
github:quartz-community/search | Git-backed source form used by the older shared engine’s house config; do not mechanically convert it. |
./plugins/example | Relative to the engine working directory; local package, not npm. Actual sandbox locals use ../plugins/sandbox-reading-stats and ../plugins/sandbox-manifest. |
enabled | Boolean: load/use this entry. Disable is not uninstall. |
options | That plugin’s own options; consult its docs. The npm font package is @quartz-community/quartz-fonts, not fonts. |
order | Processing order within plugin categories; smaller first. Not visual layout order. |
layout.position | Place a component in left, right, beforeBody, or afterBody; do not invent a slot. |
layout.priority | Visual order within a position/group; smaller first. Unrelated to date-source options.priority. |
layout.group | Named group defined under top-level layout.groups; related controls share a row/column. |
layout.condition | Named predicate such as not-index, has-tags, has-backlinks, has-toc; not inline JavaScript. |
layout.display | all, 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
| Job | Enabled plugin names |
|---|---|
| Parse + transform | note-properties, created-modified-date, description, obsidian-flavored-markdown, github-flavored-markdown, syntax-highlighting, latex, crawl-links (shortest) |
| Read + navigate | page-title, article-title, content-meta, explorer, graph, search, backlinks, breadcrumbs, table-of-contents, darkmode, reader-mode, spacer, footer |
| Emit + style | content-page, folder-page, tag-page, canvas-page, bases-page, alias-redirects, content-index (sitemap + RSS), og-image, favicon, fonts, cname |
| Visibility | remove-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.htmlguard; deletes temporary output, never movescurrent. - Lock: exclusive
flockon/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
currentswap. Comparereadlinkbefore/after; never manually edit dist/current. - Permissions:
umask 022; files must be readable, directories traversable. Never publish withumask 077or 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 assertion | Required 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 UI | A button.search-button and input.search-bar exist. Presence is not proof that a search interaction works. |
| Your change | Open 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.
| Part | Exact 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. |
| Guards | System 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_ENGINE | Optional engine path; realpath must remain under /home/loca/dev/quartz-sandbox/. Never the shared engine. |
SANDBOX_ALLOW_DRIFT | Default 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. |
| Runtime | Always 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 swap | Server 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-statsuseswordsPerMinute: 220,locale: en;sandbox-manifestdefaultsfileName: sandbox-manifest.json,pretty: true. Both receipts saysafe_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/quartzis 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
| Symptom | Real cause / fix → success |
|---|---|
403 root / 404 pages after green build | umask 077 can poison dist permissions. Publisher inspects readability/ownership, rebuilds with umask 022; no blind blanket-chmod → 200. |
| Literal wikilinks outside code; following headings swallowed | Missing blank line around raw-HTML blocks. Add the boundary blank lines, lint, republish → real links/headings. |
| Page missing | draft: 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 cells | Unescaped 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 200 | Client rendering not finished/proved. Use real Chrome with --virtual-time-budget=15000; inspect visible SVG/diagram, not just container count. |
| Pinned-sandbox plugin change absent | Forgot 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 mismatch | Another 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.