Sources

A Source is one Markdown file under paths/. Its filename gives it a name, a type, and a route; frontmatter adds metadata.

paths/index.md                 /                   type page
paths/syllabus.md              /syllabus.page      type page
paths/week-1.lab.md            /week-1.lab         type lab
paths/week-1.draft.lab.md      /week-1.draft.lab   type lab, qualifier draft
paths/labs/index.md            /labs               folder page

Create paths/week-1.lab.md:

---
title: Week 1 lab
description: Set up the toolchain.
toc:
  maxDepth: 2
---

# Week 1

Install the project dependencies and open the welcome page.

- [ ] Run `bun install`
- [ ] Run `bun run dev`

A course can query type: lab to list practical work separately from lesson explanations. The filename supplies that vocabulary; a Map adds its label/icon, and a Plugin can add domain validation or behavior. Open /week-1.lab to read this example.

Contract

Filename partMeaning
nameFirst stem segment. index makes the file its folder's page.
.qualifierMiddle segments. Kept as qualifiers, no built-in meaning.
.typeLast stem segment when there are two or more. Default is page.
.mdSource-file extension. /x.md serves authored Markdown; an untyped x.md renders at /x.page.
FrontmatterMeaning
titleOverrides the first heading as the page title.
iconA supported built-in icon name (such as pencil) or one emoji (such as ☕); defaults to the Source-type icon.
scheduleEvent start/end, independent due, and timezone; available on Sources and Blocks.
locationPhysical place Source/section and/or HTTPS online destination.
descriptionOverrides the first paragraph as the page description.
imageOverrides the first Markdown image as the preview image; relative paths resolve from this Source's folder.
typeSource type when the filename has no type hint.
layout: falseRenders without any Layout.
themeTheme tokens or a preset name for this page.
tocfalse, or minDepth and maxDepth for the table of contents.
any other keyAuthored data, available to queries as where fields.

Top-level keys are shared with installed Plugins. Core uses the metadata above; access, account, Input, Completion and other Plugins also validate their declared props. Keep domain data under a domain key to avoid collisions, for example:

profile:
  location: San Antonio, Texas
  biography: I mentor first-year students.

Here profile.location is domain text; top-level location is the structured Location contract. Arbitrary frontmatter path strings are not automatically graph relations and are not rewritten by pmx mv. Use Markdown links or a Plugin's relation contribution for references that must participate in moves and discovery.

The Core Meta plugin validates icon. It can also vary by instance: [Afternoon break](./break.event.md?iid=afternoon&icon=☕). Consumers read the resolved value from source.meta.icon; source.sourceType.icon remains the type's default, including icons supplied by the active authored Map. A Source or instance override does not change that Map definition. Icons are presentation metadata, not site favicons.

Schedules

The built-in Schedule plugin describes event times and deadlines on Sources or Blocks. Dates and local times use an explicit IANA timezone:

---
schedule:
  start: 2026-09-18T14:00
  end: 2026-09-18T14:15
  due: 2026-09-20T17:00
  timezone: America/New_York
---

# Workshop

<x-schedule-time />
<x-schedule-time field="due" format="relative" />
<x-schedule-status live="Live now" />
<x-schedule-progress />

start begins an event, end ends it, and due is an independent deadline. At least start or due is required; end requires a later start. A due-only schedule does not become an event. Elapsed event progress does not mark work complete. Authored metadata adds no controls by itself; Path task rows include available schedule times automatically.

Use ISO dates (2026-09-18) for all-day values. A date-only end or deadline includes that entire local day. Local datetimes require a timezone; ambiguous or missing daylight-saving times are rejected. An explicit offset or Z identifies an instant without a timezone. For events, date: 2026-09-18 also permits start: "14:00" and end: "14:15".

Time components accept field="start", "end", or "due". Relative format requires a field, uses the viewer's language, and falls back to an exact date for distant times. Without JavaScript, the exact authored time remains available. Status and progress update from the real clock. Use source="./event.event.md" or source="./event.event.md#session" to present another Source or Block.

Instance overrides retain other schedule fields:

[Retry](./assignment.project.md?iid=retry&schedule.due=2026-09-20T17:00)

The assignment's timezone and other defaults remain in effect. Run pmx lint (or pmx lint --format json for an agent) to diagnose invalid ranges, missing timezones, daylight-saving ambiguity, unsupported fields, and missing component targets. Use pmx query sources --has schedule to inspect scheduled Sources. Calendar feeds and external calendar synchronization are separate from these time presentations.

Locations

The built-in Location plugin describes where an activity happens. Use a local Source or section for a physical place, an HTTPS URL for an online meeting, or both:

---
location:
  place: ../locations/venue.page.md#courtyard
  online: https://example.org/join/workshop
---

# Workshop

<x-location-details />

<x-location-details /> renders the place's heading or title as a link, followed by “Join online” when supplied. The linked place Source owns directions and other venue details. Unreadable places are omitted for the current viewer. An online URL authored on a public Source is public; use Source access rules for private meeting details.

Place references resolve relative to the activity Source. Instance fields override individual values while retaining other defaults:

[Afternoon workshop](./workshop.event.md?iid=afternoon&location.place=/locations/venue.page.md%23courtyard)

Use root-relative place references in instance arguments so they remain stable when the activity moves. A move that would rebase a relative instance place is rejected with a diagnostic. Ordinary frontmatter place references follow Source moves. pmx lint checks missing places and sections, malformed local references, and non-HTTPS online URLs. pmx query sources --has location lists Sources with locations. Calendar feed generation is not provided by Location.

Notes

  • pmx lint warns on a frontmatter id; ids come from the file path.
  • A filename type hint wins over a frontmatter type.
  • index is a name, not a type, so index.lectures.md is still a folder page.

The page title, description, and image also supply the metadata used when people share a link in Messages or social apps. Set them explicitly when the opening content does not make a useful preview:

---
title: Learning to see type
description: Practice hierarchy, spacing, and contrast.
image: ./images/type-study.jpg
---

# Learning to see type

Start by comparing these two compositions.

PathMX emits an absolute rel="canonical" link and Open Graph metadata in the initial HTML, including the canonical page URL, website type, and the home Source's title as the site name when that Source is public and visible in the current View. Site-name metadata updates at rendering time without recompiling unrelated pages. Local image URLs become absolute when served or published, including a publication's configured base path. Pages with images request a large social card; pages without images request a summary. The receiving app chooses the final layout.

Use an accessible JPEG or PNG with meaningful imagery and little embedded text. Apple's rich-link guidance explains why concise titles and imagery work across preview sizes. Authored images keep their original composition; PathMX does not inspect their dimensions.

Generate a share image

The optional @pathmx/share package provides an explicit command to generate a 1200 × 630 PNG and set the page's image: metadata in the same step:

bun run pmx plugins add share
bunx playwright install chromium
bun run pmx share generate /week-1.lab

Replace the route with a public page in your site. By default, the image is saved beside its Source as week-1.lab.share.png, and the command sets:

image: ./week-1.lab.share.png

Other metadata values and the Markdown body are retained. The command replaces any previous image: value; the previous image file is left in place. Rerun the command to refresh the generated file after editing the title or theme. To choose another PNG location inside your Paths folder, use --output with a path relative to the current directory. Use URL-safe filenames without spaces:

bun run pmx share generate / --output ./paths/images/home-share.png
bun run pmx share preview / --no-open

The preview command uses the page's current image and writes an approximate light/dark card preview under .pathmx/share/. Omit --no-open to open it. Messages and social apps control the actual card layout and image cropping.

Generated images use the page title and site name with the same root → folder indexes → page theme settings and theme: false resets as the rendered page. An authored color scheme wins, then a preset's fixed scheme, then light. Reader preferences do not change the image.

Generation requires Bun and Chromium only on the machine running the command. On Linux, install Chromium's system dependencies with bunx playwright install --with-deps chromium. Use the project's installed Playwright version so its browser and renderer match. If rendering fails, the command leaves the page's metadata unchanged.

The PNG is an ordinary authored asset: keep it with the Source in version control, and it is served and included in static publication through the normal image metadata flow. Page requests, live edits, previews, and builds never generate images. A page without an image uses a summary social card.

Invalid Sources and recovery

Quote YAML text containing : , for example status: "built: shell ready". Malformed frontmatter or Block topmatter produces a Source diagnostic with file coordinates when available. pmx lint --format json reports the error, checks other Sources, and exits unsuccessfully until errors are fixed.

Development and live servers keep running when an authored Source is invalid. The affected Source is unavailable; a page depending on its Layout, Component, or include is also unavailable. Unrelated pages keep working. These requests return an uncached 503 response without exposing the invalid file's contents. Repair the file to restore availability through the normal watcher.

An invalid folder index.md blocks its subtree, including authored assets, because its access policy cannot be trusted. An invalid root index blocks all content and makes server readiness fail, while the process stays alive to detect repairs. An invalid Actor loses admission, and affected Actions cannot commit. Invalid content is never treated as a missing access policy or permission grant.

Static publication requires valid Sources. Storage failures and conflicting writer ownership can still prevent startup or writes; those are host failures, not recoverable authored-content diagnostics.