> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myme.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Conventions

> Style and structural rules for anyone extending Marfa with custom types, edges, or extensions

Marfa is extensible. Apps register their own types, edges, and extension namespaces. These conventions are how the core set looks, and following them keeps custom work readable alongside it. Some are advisory style; others are server-enforced at registration. Each rule below is marked.

## Edges

Name edges so that **"source \[edge] target" reads as an English sentence**. *(Style.)*

| Edge           | Reads as            |
| -------------- | ------------------- |
| `parent-of`    | X is parent of Y    |
| `authored-by`  | X was authored by Y |
| `in-thread`    | X is in thread Y    |
| `references`   | X references Y      |
| `attached-to`  | X is attached to Y  |
| `derived-from` | X is derived from Y |

Transitive verbs (`references`, `supersedes`) take a direct object and don't need a preposition. Everything else does. Dropping to bare nouns (`parent`, `thread`) loses direction — "X parent Y" is ambiguous — so keep the preposition.

Use kebab-case for multi-word names. Dot-namespace custom edges under your app or publisher: `app.shops-for`, `readwise.highlights`.

### Cardinality

Pick the tightest one that fits. Ask: *can the source really have many of these? Can the target?* Tighter cardinalities are enforced at edge-creation time and surface data-model mistakes early.

### Cascade

Default to `orphan`. Use `cascade` only when the target genuinely cannot exist without the source — the only core edge that cascades is `parent-of`, because a child has no meaning without its parent. For almost everything else, deleting one endpoint should leave the other in place.

See [Edges](/concepts/edges) for the full core set and their declared constraints.

## Types

**Use the right namespace.** *(Server-enforced.)* Custom types live under one of:

* `app.<app-name>.<type>` — types published by a registered app.
* `user.<type>` — personal types in your space.
* `<publisher>.<type>` — types published under a publisher handle.

The reserved roots — `core`, `system`, `app`, `user`, `marfa` — cannot be claimed as publisher handles. `core.*` and `system.*` are platform-only at registration. See [Namespaces](/concepts/namespaces) for the full grammar.

**Sibling is the default; inherit only when it earns its place.** *(Style, with server-checked `compatible-with`.)* A custom type is usually its own shape, optionally declaring `compatible-with: core.<type>`. Inherit only when every ancestor field is semantically required for your type. Children cannot redefine ancestor fields — a bad inheritance choice is hard to recover from. See [Types → Sibling vs child](/concepts/types#sibling-vs-child).

**Singular nouns.** *(Style.)* `note`, not `notes`. One item, one name.

**Subtypes earn their place.** *(Style.)* A subtype is justified when its instances share a convergent shape the parent doesn't carry, consumer apps can query or render it differently, and the differentiating fields aren't already on the parent. Otherwise prefer a sibling. See [Authoring types](/concepts/authoring-types) for the full rubric.

See [Types](/concepts/types) for the registration flow and inheritance rules.

## Property names

**`snake_case`** *(Style.)* For type fields, edge properties, and extension keys. That matches the core set (`due_at`, `starts_at`, `display_hints`, `enum_values`). Consistent casing makes filters, display hints, and SDK codegen predictable across custom and core types.

Don't prefix properties with the container they live in. A field on the `in-thread` edge is `position`, not `edge_position`. Context already tells the reader where it lives.

## Markdown body

If a type declares a markdown body field, the rendered profile is **CommonMark + GFM** *(Style.)* — no wikilinks, no Obsidian-style extensions, no custom syntax. Every client renders this profile, and anything outside it won't survive the round-trip.

This is the reason `core.note.body` and similar fields stay portable across clients. Custom types with markdown bodies should stick to the same profile.

## Extensions

Namespace extensions under your app or publisher: `<your-app>.*` or `<your-handle>.*`. *(Style.)* Put per-app annotations — sync state, reading progress, per-app metadata — in extensions rather than inflating a custom type with fields only one app uses.

Sync cursors, agent-local cache state, and other per-machine bookkeeping are **not extensions**. They live on the agent's own disk and don't attach to any item.

See [Extensions](/concepts/extensions) for the namespace grammar and permission model.

## Handles

Publishers identify themselves with a handle.

* **Format.** *(Server-enforced.)* Lowercase alphanumeric and hyphens. No leading or trailing hyphens. No consecutive hyphens. Length 3–32 characters.
* **Reserved.** *(Server-enforced.)* The reserved roots `core`, `system`, `app`, `user`, and `marfa` cannot be claimed. A list of structural words (`admin`, `api`, `support`, etc.) is reserved against squatting.

See [Handles and publishers](/concepts/handles-and-publishers) for the full policy — claim, change, account deletion, redirects.

## Scalars

A few cross-cutting conventions for primitive values across the platform:

* **IDs.** UUIDv7. Server-generated.
* **Times.** ISO 8601 with offset (`2026-04-12T14:30:00Z` or with explicit `+00:00`).
* **Tags.** Lowercase, kebab-case.
* **Languages.** BCP 47 codes (`en`, `en-GB`).
* **MIME types.** Standard IANA tokens.
* **Type identifiers.** Dotted, lowercase, snake\_case segments. No forward slashes.
