Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/scripts/ci-changes.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ runs "a script inside an agent directory" false .agents/skills/x/helper.py
runs "the issue and pull request templates" false .github/ISSUE_TEMPLATE/bug.yml .github/PULL_REQUEST_TEMPLATE.md

# What must run.
runs "a docs page, which is built into the site" true content/docs/guides/items.mdx
runs "a docs page, which is built into the site" true content/docs/build/items.mdx
runs "Markdown under content, which is built too" true content/docs/notes.md
runs "a sidebar file" true content/docs/guides/meta.json
runs "a sidebar file" true content/docs/build/meta.json
runs "source" true src/lib/source.ts
runs "a public asset" true public/logo.svg
runs "the API spec" true openapi.json
Expand Down
4 changes: 2 additions & 2 deletions .github/scripts/read-live-commit.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -465,13 +465,13 @@ verdict() { # verdict <target> <path>...
if printf '%s\n' "$@" | paths_touch_target "$target"; then printf 'publish'; else printf 'skip'; fi
}

check "docs: a content page publishes the docs site" publish "$(verdict docs content/docs/guides/x.mdx)"
check "docs: a content page publishes the docs site" publish "$(verdict docs content/docs/build/x.mdx)"
check "docs: the status source does not" skip "$(verdict docs src/status/lib/state.ts)"
check "docs: the status configs do not" skip "$(verdict docs vite.status.config.ts wrangler.status.jsonc tsconfig.status.json)"
check "docs: a mixed change publishes it" publish "$(verdict docs src/status/lib/state.ts content/docs/x.mdx)"

check "status: its own source publishes it" publish "$(verdict status src/status/lib/state.ts)"
check "status: a content page does not" skip "$(verdict status content/docs/guides/x.mdx)"
check "status: a content page does not" skip "$(verdict status content/docs/build/x.mdx)"
check "status: a docs route does not" skip "$(verdict status src/routes/mcp.ts)"
check "status: a shared component publishes it" publish "$(verdict status src/components/error-page.tsx)"
check "status: a workflow change does not" skip "$(verdict status .github/workflows/publish.yml)"
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The source of the public documentation site at `docs.marfa.so` and the status pa
## Conventions

- American English in code, comments and commits. Pages follow `STYLE.md`.
- Conventional Commits with a scope, such as `docs(guides):` or `fix(mcp):`. Work reaches `main` only through a pull request, one logical change each.
- Conventional Commits with a scope, such as `docs(build):` or `fix(mcp):`. Work reaches `main` only through a pull request, one logical change each.
- Make worktrees in `.claude/worktrees/` (`git worktree add .claude/worktrees/<name> -b <branch> origin/main`). Once the work is merged, remove the worktree and its local branch (`git worktree remove`, then `git branch -D`, because git sees a squash-merged branch as unmerged) and run `git pull --ff-only` on `main`. If git refuses a removal, report it rather than forcing it.
- TypeScript and ESM. pnpm with the committed lockfile, on Node 22.22.2 or later (24.15 or later on Node 24), which jsdom, the component tests' DOM, requires; TanStack Start, Vite and Vitest need only 22.12. CI uses Node 22.
- This repository is public. Commit no internal hostname, private IP address, personal path, key or token, in a page or anywhere else.
Expand Down
14 changes: 8 additions & 6 deletions STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ How every page on this site is written. This file is the one place the writing r
Write for a reader who is new to Marfa. Most know software; some don't, and more of them will as apps built on Marfa reach people. The Introduction and the concepts must make sense to both. Five readers come most often, and each needs something different first:

- **Someone deciding whether Marfa is for them** needs to know what it is, what it's for and what it isn't: the Introduction, then How Marfa works.
- **An app builder** needs a credential, then reading and writing items, then the model: the Quickstart, the concepts, then the Build apps and agents guides.
- **Someone running an instance** needs to start it, claim it and keep it safe: the Quickstart, then the Run Marfa guides.
- **A connector author** needs what the instance keeps for a connector, then the tutorial and the connector guides.
- **An app builder** needs a credential, then reading and writing items, then the model: the Quickstart, the concepts, then Build.
- **Someone running an instance** needs to start it, claim it and keep it safe: the Quickstart, then Run.
- **A connector author** needs what the instance keeps for a connector, then the connector tutorials and guides in Build.
- **An AI agent**, working for any of them, reads the same pages as Markdown, through the docs MCP server or `marfa docs`.

A page that a newcomer can follow serves an agent too. Never write that Marfa is only for technical people.
Expand All @@ -27,7 +27,8 @@ A page that a newcomer can follow serves an agent too. Never write that Marfa is
| Overview | Get started: the Overview, an index of the docs; the Introduction; the Quickstart | Navigation, explanation, then a tutorial |
| Overview | Concepts: How Marfa works, then one page per idea, then the Glossary | Explanation, then reference |
| Overview | AI tools: the Docs MCP server and Docs as Markdown | How-to |
| Guides | Tutorials, Build apps and agents, Build connectors, Run Marfa (ending with Troubleshooting) | Tutorial, how-to |
| Build | Build on Marfa, an index of the tab; Tutorials; Build apps and agents; Build connectors | Navigation, tutorial, then how-to |
| Run | Run Marfa, an index of the tab; Set up; Keys and access; Folders; Connectors; Look after it (ending with Troubleshooting) | Navigation, then how-to |
| Catalog | The Catalog, Items, Types (its own page, with the Core types and System types folders of one page per type under it, then Type rules and Field kinds), Edges, Events, Permissions, Connectors (its own page, with one page for each connector Marfa ships under it) | Reference |
| Reference | The Reference landing page (`/reference`): a card for each of the SDKs, the CLI, the HTTP API, the Changelog and the contract | Reference |
| Reference | SDKs and CLI: one page each, and the file the CLI keeps for other programs | How-to and reference for each client, then reference |
Expand All @@ -37,7 +38,8 @@ A page that a newcomer can follow serves an agent too. Never write that Marfa is
The tabs divide the work, and each thing is said once, with links between:

- **Overview** explains what Marfa is and how it works, with two or three examples at most.
- **Guides** show how to do things.
- **Build** shows how to write an app, an agent or a connector on Marfa.
- **Run** shows how to run an instance and look after it, and how to use Marfa without writing code.
- **Catalog** lists exactly what Marfa ships and explains nothing.
- **Reference** covers the HTTP API's operations and errors, the SDKs, the CLI, the changelog and the contract.

Expand Down Expand Up @@ -138,7 +140,7 @@ Use a component for what it's for, and only where it helps the reader.
| `Accordion` | Entries a reader opens one at a time, such as troubleshooting. Never a reference list: a list a reader looks up is a plain table on its Catalog or Reference page |
| `Files` | A directory a reader will see on disk |
| `Callout` | Something that must interrupt: an action that can't be undone, a step that loses data, or the Overview's one-line warning that Marfa is early. Every callout has a bold `title` that says what happens, and a short body under it. It draws no icon. `type="warn"` is for a loss or something that can't be undone; a neutral note takes the default, `info` |
| `Cards` | Where to go next: the Overview, an index of the docs, and the end of a tutorial or of the Introduction |
| `Cards` | Where to go next: the Overview, an index of the docs, the Build and Run landing pages, and the end of a tutorial or of the Introduction |

Write an object's fields as a Markdown table (field, type, description), not with `TypeTable`: a table reads the same on the page and in the Markdown copy.

Expand Down
8 changes: 4 additions & 4 deletions content/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ This file sits above `content/docs/` on purpose: the docs collection publishes e

## Placing a page

- Each folder directly under `content/docs/` is a tab. `(overview)/` and `(reference)/` are folder groups, so their pages sit at the site root (`/files`, `/cli`). `guides/` and `catalog/` keep their names in the URL, and so does `(reference)/api-reference/`. The Catalog tab's Types pages sit in a folder group, `catalog/(types)/`, which the sidebar shows as a closed folder named Types. Its own page is `types.mdx` (`pagesIndex` in its `meta.json`, so the folder opens on `/catalog/types`), and it holds Type rules and Field kinds and two more folder groups, `(core)/` and `(system)/`, shown as closed folders, "Core types" and "System types". No group adds to an address: a type page stays at `/catalog/note`.
- A new page needs its file and an entry in its folder's `meta.json`. `content/docs/meta.json` orders the tabs.
- A slug in `(overview)/` or `(reference)/` must not repeat a slug in either group. It must not be a name the Worker answers at the root (`api`, `assets`, `mcp`, `llms`, `llms-full`, `docs-map`, `version`), or `docs`, which the docs tools strip as a prefix.
- The Catalog tab lists names and shapes and explains nothing. Each top-level type, core or system, has a page (`src/lib/catalog.test.ts` holds them to `src/generated/catalog/builtin-catalog.json`) that takes its title and description from the type and includes the type's generated file. A core type's page ends with one `## Example` item that the test checks; Marfa writes system items itself, so their pages have none. Add a type's page, in the folder of its family and in that folder's `meta.json`, when marfa ships a type, and leave explanation to the Overview and the Guides. Include `catalog/edge-types.md`, `permissions.md` and `event-types.md` on Catalog pages only; every other page links to the Catalog page.
- Each folder directly under `content/docs/` is a tab. `(overview)/` and `(reference)/` are folder groups, so their pages sit at the site root (`/files`, `/cli`). `build/`, `run/` and `catalog/` keep their names in the URL, and so does `(reference)/api-reference/`. The Catalog tab's Types pages sit in a folder group, `catalog/(types)/`, which the sidebar shows as a closed folder named Types. Its own page is `types.mdx` (`pagesIndex` in its `meta.json`, so the folder opens on `/catalog/types`), and it holds Type rules and Field kinds and two more folder groups, `(core)/` and `(system)/`, shown as closed folders, "Core types" and "System types". No group adds to an address: a type page stays at `/catalog/note`.
- A new page needs its file and an entry in its folder's `meta.json`. `content/docs/meta.json` orders the tabs. Build, Run and the Catalog each open on an `index.mdx` that their `meta.json` lists first; a page that belongs in Build (code against an instance) or Run (an instance and its care, with no code) goes in that folder, under the group a reader would name.
- A slug in `(overview)/` or `(reference)/` must not repeat a slug in either group. It must not be a name the Worker answers at the root (`api`, `assets`, `mcp`, `llms`, `llms-full`, `docs-map`, `version`) or a tab folder (`build`, `run`, `catalog`), or `docs`, which the docs tools strip as a prefix.
- The Catalog tab lists names and shapes and explains nothing. Each top-level type, core or system, has a page (`src/lib/catalog.test.ts` holds them to `src/generated/catalog/builtin-catalog.json`) that takes its title and description from the type and includes the type's generated file. A core type's page ends with one `## Example` item that the test checks; Marfa writes system items itself, so their pages have none. Add a type's page, in the folder of its family and in that folder's `meta.json`, when marfa ships a type, and leave explanation to the Overview, Build and Run. Include `catalog/edge-types.md`, `permissions.md` and `event-types.md` on Catalog pages only; every other page links to the Catalog page.
- The header tabs come from `SECTIONS` in `src/components/section-nav.tsx`, not from `meta.json`. A new tab or a renamed landing page needs both. The Reference tab opens on `(reference)/reference.mdx` (`/reference`), the first entry in `(reference)/meta.json`, with a card for each destination below it. A new Reference page needs its entry in `(reference)/meta.json`, which the header reads, and a card there when it is a new kind of destination.
- The header holds no text links. The Changelog and the contract sit in the Reference tab's Resources group, so a new destination goes in a sidebar group, not in `links` in `src/lib/layout.shared.tsx`.
- The Reference tab's `meta.json` lifts the API pages into its own sidebar with `...api-reference`, and `src/lib/api-reference-tree.ts` finds the tab by that `meta.json` title. Renaming the title needs `REFERENCE_TITLE` changed there.
Expand Down
10 changes: 5 additions & 5 deletions content/docs/(overview)/access.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Through a **browser session**, the owner can list item summaries, manage the tra

Trash summaries also show the deletion time and actor. The **Trash** page can move, restore, purge or empty items. Those writes require a sign-in from the last five minutes. Both listings apply the instance's source filtering; an owner browser session still cannot open an item's content.

Other item content operations need a key or an app token. If a request sends a credential alongside the owner's browser session, that credential alone decides its access. No key or app ever gets the owner's authority, whatever it holds. [Sign in as the owner](/guides/sign-in-as-the-owner) shows how the owner signs in, with a password or a passkey.
Other item content operations need a key or an app token. If a request sends a credential alongside the owner's browser session, that credential alone decides its access. No key or app ever gets the owner's authority, whatever it holds. [Sign in as the owner](/run/sign-in-as-the-owner) shows how the owner signs in, with a password or a passkey.

Some owner actions need a **recent sign-in**, one from the last five minutes, such as making, changing or revoking a key, revoking an app, adding or changing a passkey, signing out every browser, or approving an app's request to write or to hold a permission.

Expand Down Expand Up @@ -62,9 +62,9 @@ The Catalog's [Permissions](/catalog/permissions) page lists them all.

## How a key or an app gets access

You make a key as the owner, or with a key or an app that holds the `keys.mint` permission, and say what it holds. A key made by another credential can hold only the permissions and maps its maker holds, and can't outlive its maker's expiry. [Manage keys](/guides/manage-keys) shows how.
You make a key as the owner, or with a key or an app that holds the `keys.mint` permission, and say what it holds. A key made by another credential can hold only the permissions and maps its maker holds, and can't outlive its maker's expiry. [Manage keys](/run/manage-keys) shows how.

**Signing in by approval is the preferred way for an app to reach Marfa.** The app registers itself and sends the owner to approve it, and it holds only what the owner approved. [Authenticate](/guides/authenticate) shows the app's side.
**Signing in by approval is the preferred way for an app to reach Marfa.** The app registers itself and sends the owner to approve it, and it holds only what the owner approved. [Authenticate](/build/authenticate) shows the app's side.

An app may also use a key. The owner can give it one, and an app that holds `keys.mint` can mint one within its own grant. A key an app mints stays within the app's grant and carries the app's `client_id`.

Expand All @@ -77,10 +77,10 @@ The **Approve access** page, or the device approval page for an app with no brow
- **Your profile:** only when the app asks for it.
- **More control over Marfa:** each permission starts off. `instance.maintain`, `schema.write` and `items.purge` are marked because they can't be undone.

Approving with everything new off **is declining**: the app gets `access_denied` and keeps what it held. Approving a write or a permission the app doesn't hold needs a [recent sign-in](#who-can-act), so the page asks the owner to [confirm it's them](/guides/sign-in-as-the-owner#confirm-its-you), and a program without one gets `403 forbidden` and no code.
Approving with everything new off **is declining**: the app gets `access_denied` and keeps what it held. Approving a write or a permission the app doesn't hold needs a [recent sign-in](#who-can-act), so the page asks the owner to [confirm it's them](/run/sign-in-as-the-owner#confirm-its-you), and a program without one gets `403 forbidden` and no code.

The owner can later **narrow the approved access**. Tokens and refresh chains that exceed the remaining grant stop working immediately, while covered tokens keep working. Giving the app wider access again requires fresh approval.

The approval stands until the owner revokes it, or until the app goes unused for the configured inactivity period. [Authenticate](/guides/authenticate) shows an app's side of it.
The approval stands until the owner revokes it, or until the app goes unused for the configured inactivity period. [Authenticate](/build/authenticate) shows an app's side of it.

The rules behind this page are in the [keys and sign-in](https://github.com/withmarfa/marfa/blob/main/conformance/spec/keys-and-oauth.md), [authentication](https://github.com/withmarfa/marfa/blob/main/conformance/spec/authentication.md), [owner summaries](https://github.com/withmarfa/marfa/blob/main/conformance/spec/search-and-filters.md#owner-item-summaries), [owner trash management](https://github.com/withmarfa/marfa/blob/main/conformance/spec/items.md#owner-trash-management), [owner counts and exports](https://github.com/withmarfa/marfa/blob/main/conformance/spec/search-and-filters.md#search-and-filtersowner-count-export), and [instance claim](https://github.com/withmarfa/marfa/blob/main/conformance/spec/instance-claim.md) chapters of the contract.
4 changes: 2 additions & 2 deletions content/docs/(overview)/connectors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ The state document, current problems and agreements belong to the key's **source

Many vendors post a webhook when something changes, so a connector can act at once. The instance can give a connector an endpoint, a private address the vendor posts to.

The instance stores each delivery as it arrived, and **the connector does the rest**: it reads the deliveries, checks each came from the vendor, and marks them handled. [Receive vendor webhooks](/guides/connector-webhooks) shows how.
The instance stores each delivery as it arrived, and **the connector does the rest**: it reads the deliveries, checks each came from the vendor, and marks them handled. [Receive vendor webhooks](/build/connector-webhooks) shows how.

## Building one

[Build a connector](/guides/connector) walks through a connector from a template, and [Make a connector follow changes](/guides/connector-follow-changes) teaches it to archive what the vendor drops and report a failed run. The six connectors Marfa ships, listed in the [Catalog](/catalog/connectors), are working examples in [withmarfa/marfa-connectors](https://github.com/withmarfa/marfa-connectors), and [Run a connector](/guides/run-a-connector) runs any of them. A connector for a Google service signs in through a project you create yourself, as [Create a Google Cloud project for Google connectors](/guides/google-project) shows.
[Build a connector](/build/connector) walks through a connector from a template, and [Make a connector follow changes](/build/connector-follow-changes) teaches it to archive what the vendor drops and report a failed run. The six connectors Marfa ships, listed in the [Catalog](/catalog/connectors), are working examples in [withmarfa/marfa-connectors](https://github.com/withmarfa/marfa-connectors), and [Run a connector](/run/run-a-connector) runs any of them. A connector for a Google service signs in through a project you create yourself, as [Create a Google Cloud project for Google connectors](/run/google-project) shows.

The rules behind this page are in the [connectors](https://github.com/withmarfa/marfa/blob/main/conformance/spec/connectors.md) and [inbound webhooks](https://github.com/withmarfa/marfa/blob/main/conformance/spec/inbound-webhooks.md) chapters of the contract.
Loading
Loading