Typed pages and properties
Every page has a type and a set of properties. Both are authored in YAML frontmatter — frontmatter is the only authoring surface — and the server projects them into typed Postgres columns used for routing, queries, and table views. You never set them through a separate UI; you write them in the page source and save.
Page type
The type: frontmatter key sets the page type. It must be a slug matching
^[a-z][a-z0-9_-]{0,31}$ (lower-case, starts with a letter, ≤32 chars). Omit
type: and the page defaults to page.
type: issueIf type: is present but not a valid slug, the page falls back to page and the
raw value is preserved as an ordinary property (type) so nothing is silently
lost.
System types
Three types are built in:
page— the default. A plain document; any frontmatter you add becomes a property.issue— a tracked item. Carries a small field schema (see below) and shows up in tables.table— a page that renders a live view over other pages. See Tables.
Custom types
Any other slug that matches the rule (type: meeting, type: customer, …) is a
custom type. Custom types have no field schema — every key you write is just a
property — but they still group and filter in table views, so you can build a
type: meeting table without any extra setup.
Properties
Every visible frontmatter key that isn't type: (or a reserved key, see below)
becomes a property. Properties are stored as JSON and shown in the page's
Properties panel.
type: issue
status: open
priority: 1
due: 2026-07-01
tags: [backend, urgent]
owner:
name: Maya
email: maya@example.comYAML dates are normalized to ISO-8601 strings; numbers, booleans, arrays, and
nested maps are preserved. null/empty values are dropped.
Limits
Properties have shape caps; exceeding any one rejects the save with a typed
PROPS-01 error (no truncation):
- ≤ 64 properties
- property keys ≤ 64 characters
- nesting depth ≤ 3
- ≤ 16 KiB serialized
Issue field schema
When type: issue, a few fields are schema-checked. Writing one with the wrong
kind rejects the save with PROPS-02:
| Field | Kind | Notes |
|---|---|---|
status | text | Defaults to open when you omit it — the default is written back into the served frontmatter so the file is self-describing. |
priority | number | |
due | date | ISO-8601 / YAML timestamp. |
assignee | relation → user | Must be a workspace member, else the save is rejected (PROPS-02). |
labels | text array |
Field values (the actual status names, label strings, etc.) are unvalidated in
v1 — views group and filter on whatever you write, so Open and open are
treated as the same value (string compares are case-insensitive).
System-computed vegastack_* keys
On every save the server stamps a block of read-only vegastack_* keys into the
served source so a downloaded/exported file is self-describing. These are
reserved: they are excluded from properties, stripped from the canonical body
on save (editing them is a no-op), and re-stamped from the database on the next
serve. The database is always the source of truth — never read these back for
routing or auth.
vegastack_page_id
vegastack_workspace_id
vegastack_space_id
vegastack_folder_id
vegastack_slug_id
vegastack_source_type
vegastack_template_id
vegastack_short_id
vegastack_created_at
vegastack_updated_at
vegastack_created_byThe last four (vegastack_short_id, vegastack_created_at,
vegastack_updated_at, vegastack_created_by) are stamped onto the served
markdown for MCP fetch, CLI get, and export so "give me this page" returns one
complete file.
Authoring a typed page
The New page dialog lets you pick a type (Page or Issue) — it writes the
type: key into the new page's frontmatter through the normal create path. You can
also write type: yourself in any page's source and save; the projection picks it
up. There is no separate "convert to issue" action — the type is whatever the
frontmatter says.