Local disk structure
All documents you create in Moment are stored as markdown files on your local device. There is no database, no proprietary format, and no server required to read or edit them. This page describes how documents are organized on disk.
The documents directory
All Moment docs live in the ~/.moment/documents/ directory. Each document is stored in its own subdirectory, named with a unique identifier:
Each of these directories is a self-contained Git/Jujitsu repository.
Anatomy of a document
Inside a document directory, you will find the following structure:
Page folders are named after the page's slug — a human-readable handle derived from the page title. Older documents created before Moment v1.3 may still use the 25-character page ID as the folder name; see Page folders and readable slugs below for details.
moment.yml
The moment.yml file is the document manifest. It contains metadata about the document and all of its pages. A typical moment.yml looks like this:
Key fields in moment.yml:
id: The unique identifier for the document.
schemaVersion: The version of the document schema.
title: The display title of the document.
created: The timestamp when the document was created.
pagesMetadata: A list of all pages in the document. Each entry contains:
id: The unique page identifier. This is the stable, permanent handle for the page — internal links and parentPageID references use it under the hood, so links never break when a page is renamed.
slug: The URL-friendly slug used for routing. This also determines the page's folder name on disk (see below).
title: The display title of the page.
archived: Whether the page is archived.
parentPageID: The id of the parent page, or null for top-level pages. This defines the page hierarchy shown in the sidebar.
path: (optional) A custom path to the page file, relative to the document root. If omitted, the default location is used (see below).
type: (optional) The page type. Can be markdown (default), canvas, or request. Request pages provide a Postman-style interface for building network requests and are stored as .yml files instead of .md.
If the document has been configured for publishing, moment.yml will also contain a publishSettings section with template and URL configuration.
The pages/ directory
The pages/ directory contains one subdirectory per page. To find a page on disk:
Look up the page in moment.yml by its id or slug.
If the page has a path field, the file is at that path relative to the document root.
Otherwise, the default location for markdown pages is pages/<slug>/index.md. For request pages, it is the same path but with a .yml extension instead of .md.
Page folders and readable slugs
In Moment v1.0, every page you created was an index.md file stored inside a pseudo-random 25-character folder name. A new page, for example, could live at:
That identifier works, but it's unreadable. You can't tell what a page is about from its folder name, you can't skim a file tree, and when a Moment document is checked into git, diffs and commit histories are peppered with opaque IDs instead of meaningful names.
As of Moment v1.3, page slugs are human-readable by default, derived from the page title. Creating a page titled "Blog Post [DRAFT]" will generate a slug like blog-post-draft, and the page will live at:
Slugs are normalized automatically: lowercased, spaces and punctuation replaced with dashes. Duplicates are disambiguated with a numeric suffix (blog-post-draft-2).
Why this matters:
Legible file trees. Open a Moment document in your editor and the folder layout reads like a table of contents.
Meaningful git history. git log and PR diffs show pages/press-release-draft/index.md instead of pages/dpdluqzizwmph9kycluiaw408/index.md.
Shareable URLs. Links you paste into Slack, email, or docs convey what they point to before anyone clicks.
Better agent ergonomics. Tools like Claude Code can navigate a document by topic rather than by guessing which random ID corresponds to which page.
Every page still has a stable underlying id — the 25-character identifier described in the moment.yml section above. That ID is what internal links, parentPageID references, and the manifest use under the hood, so links never break when a page is renamed. The slug is purely the human-facing handle; the ID remains the source of truth.
Existing documents keep their current slugs until you opt in to regenerate them, so nothing breaks on upgrade. This is why you may still see 25-character folder names inside older documents alongside readable ones.
index.md
Every Markdown page directory contains an index.md file. This is the page content, written in standard Markdown. This is the file you are editing when you type in the Moment editor, and it is the file that gets rendered when someone views the page.
The related/ directory
Some page directories contain a related/ subdirectory. This is where page assets such as images are stored. When you add an image to a page in the Moment editor, the image file is placed in the page's related/ directory and referenced from index.md.
Each document is a Git repository
Every document directory is initialized as a Git repository. This means:
Full version history. Every committed change is recorded and can be inspected with git log.
Diffs and rollback. You can use git diff to see what has changed, and standard Git operations to inspect or revert changes.
Branching and merging. The Git repository supports all standard Git operations.
Because page folders are named after their slug, the history you see through these tools stays legible — a rename of "Draft intro" to "Introduction" surfaces as a rename from pages/draft-intro/ to pages/introduction/ rather than an unchanged opaque ID.
The Moment desktop app provides a built-in UI for committing changes and viewing history (see Committing changes), but you can also interact with the repository directly from the command line.
AGENTS.md file
When creating a new document, we also add an AGENTS.md specific to Moment documents, which is versioned. We will update this for you unless you provide explicit permission to do so.
Understanding the differences between id, slug, and title in moment.yml
As of Moment v1.3, the following applies:
Every page entry in moment.yml carries three identifiers that look related at a glance but do very different jobs:
id — the stable, internal identity of the page.
A pseudo-random 25-character string, assigned once at page creation and never changed. The source of truth everywhere it matters internally: parentPageID references, internal links of the form /d/local/<docID>/<pageID>, diagnostics snapshots under .moment/diagnostics/<pageId>/, and runtime error log entries (page=<id>). Because it never changes, renaming a page, moving it in the tree, or retitling it cannot break any existing link.
slug — the human-facing, renameable handle for the page.
Derived from the page title (lowercased, with spaces and punctuation turned into dashes). Duplicates get a numeric suffix like -2. Used for the URL path and, by default, the on-disk folder name (pages/<slug>/index.md). Regenerated when you rename a page — which means a slug is not safe to rely on as a permanent reference.
title — the display name of the page.
Free-form text, exactly as the author typed it: mixed case, spaces, punctuation, emoji, brackets — all allowed (Blog Post [DRAFT] 🚀).
What appears in the page header, the sidebar, browser tabs, and search results. It is the only one of the three a reader actually sees as prose.
The seed for the slug, not a duplicate of it. title: "Blog Post [DRAFT]" becomes slug: blog-post-draft — the slug is a URL-safe projection of the title, stripped of anything a filesystem or URL bar would choke on.
Like the slug, the title is mutable; unlike the slug, editing it does not automatically move the page on disk. Slug regeneration is its own explicit action.
Put together, the three fields form a small hierarchy of stability: id never changes, slug changes when you regenerate it, title changes whenever you feel like it. The id is what the system uses to remember which page; the slug is what the filesystem and URL bar use to locate it; the title is what a human uses to recognize it.
What stays the same
Every page still has a stable underlying id — the 25-character identifier you see today. That ID is what internal links, parentPageID references, and the moment.yml manifest use under the hood, so links never break when a page is renamed. The slug is purely the human-facing handle; the ID remains the source of truth.
Existing documents keep their current slugs until you opt in to regenerate them, so nothing breaks on upgrade.