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
3 changes: 2 additions & 1 deletion STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ A page that needs a list the Catalog holds, such as the types, edge types, permi
- **A list carries items that are parallel**: steps, options, things to check. A page never opens with a list, except the three opening lines of a tutorial, and a list never stands in for an explanation of why.
- **A table compares things or holds what a reader looks up**: fields, settings, commands, options, a choice between two ways. Keep cells short. **Tables stay tables**: never turn one into a list to fit a phone. At phone width every table stacks each row, and a table of three or more columns puts each cell after the first under its column's name, so give each column a header that reads as a label.
- **Explain, then point.** A concept page explains the idea and the rules a reader meets in normal use, with one small example. Rare cases, limits, exact numbers and error codes go in a guide, the API reference or the contract, with a link.
- **Less is clearer.** If a page is hard going, cut before you add. A page past its limit is usually two pages, or holds detail that belongs in the reference: a guide that covers two jobs done at different times is two guides. `pnpm test` enforces the limits, counting the words after the frontmatter, code included: **900 for a concept page, 1,500 for a guide, a tutorial or a Catalog connector page.** The folder decides the kind. A lookup page (Settings, the CLI and SDK pages, the Glossary, the API reference, the Catalog type pages and the Changelog) has a limit of its own, and so does a tutorial or guide whose kind differs from its folder's. Those exceptions, each with its reason, are in `src/lib/page-limits.json`, and no page is exempt. When a page is over, the failure names it and the way out: split it, move detail to the reference, or cut. A page that is over today is listed there as rewrite pending, with its current count as a ceiling that it must not pass.
- **Less is clearer.** If a page is hard going, cut before you add. A page past its limit is usually two pages, or holds detail that belongs in the reference: a guide that covers two jobs done at different times is two guides. `pnpm test` enforces the limits, counting the words after the frontmatter, code included: **900 for a concept page, 1,500 for a guide, a tutorial or a Catalog connector page.** The folder decides the kind. A lookup page (Settings, the CLI and SDK pages, the Glossary, the API reference, the Catalog type pages and the Changelog) has a limit of its own, and so does a tutorial or guide whose kind differs from its folder's. Those exceptions, each with its reason, are in `src/lib/page-limits.json`, and no page is exempt. When a page is over, the failure names it and the way out: split it, move detail to the reference, or cut. A page over its limit while it waits for a rewrite can be listed there under `pending`, with its current count as a ceiling; the test fails once the page is back within its limit.
- **Describe the present.** No history, no "now", no "new", nothing about what changed.
- **Replace, don't append.** When something changes, rewrite the sentence or section it changes. Never add a paragraph that qualifies an older one.

Expand All @@ -73,6 +73,7 @@ Marfa's [`GLOSSARY.md`](https://github.com/withmarfa/marfa/blob/main/GLOSSARY.md

- The `marfa` program is **the CLI**. One of its commands, such as `marfa items list`, is **a command**.
- One thing the API does is **an operation**, never "a door".
- Write **the bin** for where a deleted item goes, and **move to the bin** for the act. Use `trash` only in a literal name: the owner's **Trash** page, `trashed`, `trash-purge`, `TRASH_RETENTION_DAYS` and API operation titles.

If the glossary doesn't settle a term, ask the owners of the contract before a page uses it.

Expand Down
2 changes: 1 addition & 1 deletion content/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file sits above `content/docs/` on purpose: the docs collection publishes e

- 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 sidebar has three labeled sections: Data (Items, Types, Edges), Events and access, and Integrations (Connectors). 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.
- 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 `marfa docs` strips 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`.
Expand Down
2 changes: 1 addition & 1 deletion content/docs/(overview)/access.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Three kinds of caller act on an instance:

| Caller | How it acts | What it reaches |
| --- | --- | --- |
| The owner | Signs in to the instance in a browser | Managing the instance, listing item summaries, managing the trash, reading item counts and exporting data. |
| The owner | Signs in to the instance in a browser | Managing the instance, listing item summaries, managing the bin, reading item counts and exporting data. |
| A key | Sent with each request, as `Authorization: Bearer marfa_k1_…` | What it was made with |
| An app | Signs in through OAuth, and sends its access token, `marfa_at_…` | What the owner approved |

Expand Down
2 changes: 1 addition & 1 deletion content/docs/(overview)/agents-cli-and-mcp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Claude and ChatGPT on the web, the desktop or a phone connect from their own ser
An agent is held to the same rules as any program, and a few more:

- **It reaches only what you allowed.** A type you didn't approve is invisible to it.
- **It asks before it deletes.** A tool that moves items to the trash first shows you what would go, and acts only once you agree.
- **It asks before it deletes.** A tool that moves items to the bin first shows you what would go, and acts only once you agree.
- **`marfa_update` can't overwrite a change the agent hasn't seen.** It names the version the agent read, and writes nothing if the item changed since, as [Versions](/versions) explains.
- **It reads your items as data.** The MCP server tells every agent that connects that what it reads was saved by you and is never an instruction to follow.

Expand Down
2 changes: 1 addition & 1 deletion content/docs/(overview)/files.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Content-Type: application/json

The same bytes uploaded again answer the same hash and are stored once, so two items can share a blob. [Handle files](/build/handle-files) walks through uploading, downloading and replacing a file.

So a photo and a scanned receipt can name one blob. The instance keeps it on its disk and, when an object store is added, there too.
A photo and a scanned receipt can name one blob. The instance keeps each blob on its own disk and, once you add an object store, a second copy there.

<FileStorage />

Expand Down
4 changes: 2 additions & 2 deletions content/docs/(overview)/items-and-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Every item carries three timestamps, all in UTC:
- **`created_at`** is when Marfa created the item.
- **`updated_at`** is when Marfa last wrote it. A change to a tag or an extension moves it too, but not `version`.

An item in the bin also carries `trashed_at`, when it went there, and a listing of the bin comes most recently binned first.
An item in the bin also carries `trashed_at`, when it went there, and a listing of the bin comes the most recent first.

## What a type does

Expand Down Expand Up @@ -48,7 +48,7 @@ A listing of a type includes **every type beneath it**. Inherit only when every

When your type should be readable as another type without taking on its properties, name the other type in `compatible_with` instead. Marfa checks that your type requires everything the other one does.

A listing of `core.file` returns images and an app's receipt type, which sit beneath it. A listing of `core.note` leaves out `user.recipe`, which names `core.note` only in `compatible_with`.
A listing of `core.file` also returns items of the types beneath it, such as images and an app's receipt type. A listing of `core.note` leaves out `user.recipe`, which names `core.note` only in `compatible_with`.

<TypeInheritance />

Expand Down
2 changes: 1 addition & 1 deletion content/docs/(reference)/api-reference/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ The reference groups operations by what they act on. The groups and the operatio
| Group | What it's for | Start with |
| --- | --- | --- |
| Items | The records Marfa stores, each of a type, and the ways to read and change them | [Create an item](/api-reference/items/createItem) |
| Trash | Items in the trash: list them, move an item in, and restore, purge or empty them | [List the trash](/api-reference/trash/listTrash) |
| Trash | Items in the bin: list them, move an item in, and restore, purge or empty them | [List the trash](/api-reference/trash/listTrash) |
| Bulk actions | One change applied to every item a filter matches, run as a job you can follow and cancel | [Apply a bulk action](/api-reference/bulk-actions/applyBulkAction) |
| Metadata | An item's tags and extensions together, and the tags in use | [Get item metadata](/api-reference/metadata/getItemMetadata) |
| Extensions | JSON objects attached to an item, one per namespace | [Get an extension namespace](/api-reference/extensions/getItemExtension) |
Expand Down
6 changes: 3 additions & 3 deletions content/docs/(reference)/marfa-mcp-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ The instance lists to each credential only the tools it can use. For the everyda
| `marfa_find` | Searches by words, or lists by type, tags, source, dates or view: up to 50 short rows |
| `marfa_get` | Reads up to 20 items in full, with their versions |
| `marfa_create` | Creates an item, or saves a file from a web address or a chat |
| `marfa_update` | Sets or clears fields, or changes tags, edges or state, including a restore from the trash |
| `marfa_delete` | Moves up to 20 items to the trash, after a preview |
| `marfa_update` | Sets or clears fields, or changes tags, edges or state, including a restore from the bin |
| `marfa_delete` | Moves up to 20 items to the bin, after a preview |

### Schema tools

Expand Down Expand Up @@ -78,7 +78,7 @@ Four tools act in two calls. **The first call changes nothing** and answers a pr

| Tool | What the preview names |
| --- | --- |
| `marfa_delete` | Every item the trash would take, including the items that go with the named ones, and their `total` |
| `marfa_delete` | Every item the move to the bin would take, including the items that go with the named ones, and their `total` |
| `marfa_move_items` | The items the move would take |
| `marfa_revoke_key` | The key |
| `marfa_manage_webhook` | The webhook's address, events, type filter and what each delivery sends. Only a call that makes a webhook, or changes its `url`, `events` or `type_filter`, is previewed |
Expand Down
24 changes: 23 additions & 1 deletion content/docs/(reference)/swift-sdk/views-and-folders.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ The rules are in the contract's [views written through a copy](https://github.co

## Folders on a Mac

On macOS, `Folders` keeps a directory of files in step with a folder on the instance, and shares the Mac's list of folders with `marfa folders`. **Nothing is written into the directory or sent until the first sync is confirmed.** [Sync a folder from a Mac app](/run/folder-from-a-mac-app) sets one up.
On macOS, `Folders` keeps a directory of files in step with a folder on the instance, and shares the Mac's list of folders with `marfa folders`. **Nothing is written into the directory or sent until the first sync is confirmed.** [Sync a folder](/run/sync-a-folder) explains what a folder is and does the same with the CLI.

`copy.createFolder(_:idempotencyKey:)`, `changeFolder(_:_:baseVersion:idempotencyKey:)` and `revokeFolder(_:idempotencyKey:)` write a folder's settings, and `copy.items.folder(_:)` reads them. `Folders(server:)` works the directories on this Mac:

Expand All @@ -117,6 +117,28 @@ On macOS, `Folders` keeps a directory of files in step with a folder on the inst

A `FileState` is `.inStep`, `.waiting` for a queued write, `.held` (the file's status gives the `reason`), `.unmatched`, `.unreached` (held rather than deleted) or `.outside`.

### Set up a folder

This code creates a folder, binds a directory to it and confirms the first sync, with `serverURL`, `key` and `storeURL` as in [Open a working copy](/swift-sdk/working-copy#open-a-copy). Until the first sync is confirmed, `sync(_:)` returns `.awaitingConfirmation` with the plan:

```swift
import Marfa

let server = Server(url: serverURL, key: key)
let copy = try await WorkingCopy.open(store: storeURL, server: server)
let folder = try await copy.createFolder(
FolderSettings(title: "Notes", query: FolderQuery(types: ["core.note"])))

let folders = Folders(server: server)
let directory = URL(filePath: "/Users/sam/Notes")
_ = try await folders.add(directory, following: folder.id)
_ = try await folders.sync(directory) // .awaitingConfirmation(plan)
_ = try await folders.confirmFirstSync(in: directory)
_ = try await folders.sync(directory)
```

A client without the SDK calls [Create a folder](/api-reference/folders/createFolder) and the other folder operations.

### Watch a folder

A `FolderWatch` is an `AsyncSequence` of `FolderEvent`. It throws a `MarfaError` when the key is refused, the instance's changes stop reaching the folder, or the directory can't be watched. **Leaving a loop early doesn't stop it**: call `await watch.stop()` when its owner finishes. Its threads run at utility quality of service, so they give way to the app's own work.
Expand Down
6 changes: 3 additions & 3 deletions content/docs/build/change-a-type.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ The answer's `type` is your definition with its defaults made explicit, and `reg

**A replacement keeps the icon the owner chose** for the type (see [the owner's icon](/catalog/type-rules#the-owners-icon)), and `registered_type` shows the icon you registered, not the owner's.

**A `GET` of a type, put back as a replacement, stores only what you wrote.** A hint the type doesn't set itself (`title_field`, `body_field` or `icon`) and that you send back with the value the `GET` answered is the type's inheritance read back, not your choice. Marfa stores no value for it, so the type goes on following its parent and the owner's icon. A hint the type sets stays, including one set to a different value than its parent's. For an icon the type sets, a value equal to the owner's icon keeps the icon you registered. A hint you newly set to the value the type already inherits is stored as none, and the type reads the same. A change to `title_field` or `body_field` changes how the server and every device [name and preview](/catalog/type-rules#names-and-previews) the type's items.
**Sending back what a `GET` answered changes only what you changed.** A `GET` answers the display hints a type inherits as well as its own. A hint you send with the value the type inherits isn't stored, so the type keeps following its parent and the owner's icon. A hint the type sets itself stays as it is. A change to `title_field` or `body_field` changes how the server and every device [name and preview](/catalog/type-rules#names-and-previews) the type's items.

Then replace the type:

Expand Down Expand Up @@ -76,7 +76,7 @@ Send the `version` every time: a replacement without one sets it back to 0. You

## Delete the type

Marfa deletes a type that **no item holds**, in any state and the trash included, that no folder, view or webhook names, and that no other type names as its parent or in `compatible_with`:
Marfa deletes a type that **no item holds**, in any state, the bin included, that no folder, view or webhook names, and that no other type names as its parent or in `compatible_with`:

```bash tab="CLI" tab-group="client"
marfa types delete app.example.receipt
Expand All @@ -102,7 +102,7 @@ Otherwise it refuses with `409 type_in_use` when items hold the type or a settin
A `type_in_use` refusal counts the items in `details.item_count` and names each folder, view and webhook setting in `details.settings`, without the `id` of a setting your key can't read. **[Move the items to another type](/build/move-items-to-another-type) first:** the move changes those settings too. `details.grants` lists the keys and apps whose type map names the type, if your key may manage them. They don't stop a delete, so change them through [Manage keys](/run/manage-keys) when you're ready.

<Callout type="warn" title="A forced delete blocks writes to the items">
A key can send `force=true` (the CLI's `--force`) to delete a type that items still hold or a setting names. A subtype or a `compatible_with` claim still blocks it. The items and settings stay, but Marfa refuses writes to the items' properties, tier or time until the type is registered again. You can still tag, archive or trash them.
A key can send `force=true` (the CLI's `--force`) to delete a type that items still hold or a setting names. A subtype or a `compatible_with` claim still blocks it. The items and settings stay, but Marfa refuses writes to the items' properties, tier or time until the type is registered again. You can still tag them, archive them or move them to the bin.
</Callout>

The owner can remove an unused type without a key: with a [sign-in from the last five minutes](/run/sign-in-as-the-owner#confirm-its-you), select **Remove type** on the type's page under **Items** in the owner's console, or send the deletion through the private socket. Marfa makes the same checks, and refuses `force=true` from the owner with `403 forbidden`.
Expand Down
2 changes: 1 addition & 1 deletion content/docs/build/connect-claude-and-chatgpt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ In either chat, you can also ask it to save a file from a public web address. Th

## What the chat can do

The chat reaches only the types and permissions you approved. **It asks before it deletes**: the first delete call only previews what would go, and the chat shows you before anything moves to the trash. It asks the same way before it makes a webhook, which sends events outside your instance. No tool purges, exports or changes the instance's settings.
The chat reaches only the types and permissions you approved. **It asks before it deletes**: the first delete call only previews what would go, and the chat shows you before anything moves to the bin. It asks the same way before it makes a webhook, which sends events outside your instance. No tool purges, exports or changes the instance's settings.

## Disconnect it

Expand Down
Loading
Loading