Project map
MarkStack V2 has three distinct kinds of files: source you maintain, generated reader output you deploy, and private runtime data used by the optional editor. Understanding that split prevents the two most expensive mistakes: losing source by treating a build directory as permanent storage, and exposing private data by uploading the whole project.
The example installation is Field Guide. Paths below describe a checkout, not a requirement to place your files under a particular server directory.
The main directories
| Path | Purpose and upload rule |
|---|---|
content/ |
Markdown/landings; keep source, do not upload to reader |
static/ |
Public assets; review their copied output |
templates/ |
Page assembly; keep source, do not upload |
| Site config | siteconfig.json: reader policy/branding; no source upload |
build.js, lib/ |
Build/server code; no reader upload |
dist/ |
Generated reader; upload contents only |
| Dependencies | node_modules/; no reader upload |
.markstack/ |
Default private runtime; never upload the directory |
The source files are valuable even when readers do not need them. Keep them under your own review/backup process. npm run clean removes the default dist/; a normal build removes its selected output after URL preflight. Neither is a source-history system.
Anything under static/ is publishable input, including files that are not images or recognized text. Do not put account data, backups or confidential notes there. A Git ignore rule controls Git tracking, not what the builder copies or a host serves.
Private runtime and generated public subsets
| Artifact | Contents and disclosure |
|---|---|
| Accounts | users.json: hashes/permissions, always private |
| Settings | Safe branding overrides; selected values render publicly |
| Header links | links.json: shown links render publicly |
| Icons | favicons/: selected shown icons copied publicly |
| Audit | audit.jsonl: operational events, private |
| Publish state | Fingerprint/time/count, private |
Settings filename: site-settings.json. Publication marker filename: publish-state.json.
The editor locates runtime files beside its users file. A build may select a different data directory. Copying selected public settings into output does not mean the runtime directory is safe to upload. Unix-created user/audit files use mode600; settings/links/icon files use640. Those file modes do not replace private directory ownership, Windows ACLs, backup controls or review of an existing file’s permissions. See Storage and formats for schemas and recovery.
A small content tree
content/
_index.md
guides/
_index.md
hello.md
static/
images/
workstation.png
content/_index.md is home content. guides/_index.md supplies the folder landing page and section metadata. hello.md is a leaf page. With the titles used in First site, the guide becomes /guides/hello/.
Folder/page titles control title-derived routes. The filename is still important for storage and editing, but an explicit frontmatter title can make the public route different from the filename. Changing a section title can move all descendant URLs. Keep a route inventory before reorganizing a published handbook.
An image at static/images/workstation.png is copied as /images/workstation.png for a domain-root site. The Markdown link must refer to the output URL, not the source path static/images/.... Subpath hosting needs consistent base-URL handling and actual browser checks.
Source becomes a reader
flowchart LR C[Markdown and public assets] --> B[MarkStack V2 build] T[Configuration and templates] --> B B --> D[Generated output] D --> H[Static hosting] H --> R[Readers]
The builder reads Markdown and assembles HTML using templates, configuration and navigation. It also generates search data and copies public assets. Readers need those generated files and a host that serves them; they do not need Node.js, your Git checkout or the editor process.
Editing dist/index.html may seem to work until the next build. It is output, so the change is not durable. Put the change in Markdown, configuration or a supported template/static source, rebuild and inspect the result. Likewise, deleting a source page does not remove an already deployed copy until you rebuild and replace the host’s output correctly.
Separate build input/output deliberately
The default build uses the project’s content/ and dist/. A controlled alternate build can use:
npm run build -- --content ./docs --out ./review-output --data ./docs-data
Npm forwards arguments after --. These options select the content, output and runtime-data directories. They do not move the project templates/static/configuration into the alternate content tree. Relative paths are resolved for the process; verify the exact locations before using them.
URL collision preflight happens before normal output deletion, but this is not a general transaction or rollback guarantee. Once that preflight passes, a later generation/copy error can leave an incomplete directory. The output directory is cleared. Never point --out at source, private data or a folder holding unrelated files. Use a new dedicated review directory and check that it is the one you intend to replace.
npm run serve previews the project’s default dist/, not an arbitrary custom --out folder. If you built into a separate output, choose an appropriate HTTP preview server for that directory or build the intended local preview back to dist/. Do not inspect an old dist/ and assume it is your alternate build.
The optional editor’s private data
By default the account store is .markstack/users.json. A service can choose another location with MARKSTACK_USERS_FILE. The editor keeps its runtime data beside its selected account store. Build data selection uses --data, then MARKSTACK_DATA_DIR, then the parent of MARKSTACK_USERS_FILE, then the project default.
These controls have different jobs. A build-only --data flag does not redirect the editor’s account store. Align account CLI, editor service and build selection so you do not create an account in one directory and start a server using another.
Private data includes password hashes and account state, plus operational history and runtime settings. Some public-facing settings can influence generated branding/links, but that is not permission to copy the account directory into output. Preserve private files with restricted access and keep them outside a public web root.
Keep a source history and a restore plan
Back up Markdown, public media, configuration, custom templates/styles and private editor runtime data. Record the engine commit, Node requirement, lockfile and build procedure. Git is useful for source history, but it is not automatically a complete backup of every private runtime file.
Generated output is useful as a reader rollback snapshot. It cannot reconstruct all source/customizations or restore accounts. Browser drafts are local recovery conveniences, not backups. See Backup and restore for a separate rehearsal.
Check the upload boundary
Before sending a reader to any host:
- Build into a known output directory.
- Inspect its root and deep page routes through HTTP.
- Verify search/assets/media and local dependencies.
- Review all content for its intended audience.
- Confirm no private data, source backups or editor endpoints are included.
- Upload only that checked output, with
index.htmlat the host root.
The static reader has no built-in authentication. Hiding a link, leaving a section out of navigation or choosing an obscure URL is not access control. If the handbook is internal, protect its host/network and verify unauthorized readers are actually denied.