Skip to content
16 changes: 12 additions & 4 deletions content/docs/(overview)/access.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ 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, reading item counts, and exporting data |
| 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. |
| 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 |

Every other item operation needs a key or an app's token. A request that sends one is judged by that credential alone, even from a signed-in browser, and no key or app gets the owner's authority. [Sign in as the owner](/guides/sign-in-as-the-owner) shows how.
Through a **browser session**, the owner can list item summaries, manage the trash, read item counts and export data without making a key. An item summary gives its ID, type, version, name, source and creation time, but no properties, metadata, history or edges. The **Items** page searches names, not item content.

Trash summaries also show the deletion time and actor. The **Trash** page can move, restore or purge 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.

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

Expand Down Expand Up @@ -71,6 +75,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. The approval stands until the owner revokes it, or the app goes unused for a year, a limit [Settings](/guides/settings) can change with `MARFA_GRANT_INACTIVITY_DAYS`.
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.

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 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 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.
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.
32 changes: 31 additions & 1 deletion content/docs/(reference)/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,37 @@ description: What each release of Marfa changed for someone running an instance

Each release of Marfa ships the server and its container image, the CLI for macOS on Apple silicon, and the TypeScript SDK on npm. This page lists every release, newest first, with what changed and a link to the full list on GitHub. The Swift SDK's own releases are listed after Marfa's.

**Marfa doesn't upgrade a database in place.** A build refuses a database whose schema differs from its own, or that holds retired tables or settings, and leaves the file unchanged; a version change alone doesn't cause a refusal. [Upgrade](/guides/upgrade) shows how to move your data with an export and a restore.
**Marfa doesn't upgrade a database in place.** A build refuses a database whose schema differs from its own, or that holds retired tables or settings, and leaves the file unchanged; a version change alone doesn't cause a refusal. [Upgrade](/guides/upgrade) shows how to test an export and restore on fresh storage before switching builds.

## v0.0.21

Released October 21, 2026. [v0.0.21 on GitHub](https://github.com/withmarfa/marfa/releases/tag/v0.0.21)

**This release changes the database schema.** It refuses an existing v0.0.20 database without changing it. Keep the old build, database and an export made with that build. Test restoring the export into fresh storage before replacing the running instance; cross-build restore is not guaranteed. [Upgrade](/guides/upgrade) covers the process.

For the owner's console:

- **Items and Trash work with summaries.** Items lists and searches names by type without opening item content. Names follow the type's display hints and fallback fields. Trash shows who moved an item there, when, and when it becomes eligible for deletion. You can move items to the trash, restore or permanently delete them, empty the trash in batches, and remove unused custom types. Item actions check the version you saw. These changes ask for a recent sign-in.
- **Connected apps lets you reduce access.** Saving a reduction ends tokens and refresh chains that exceed it; tokens already within the reduced access keep working. Increasing access requires fresh approval. The app-access operations are also published in the API reference.
- **Folders shows reported copies and their problems.** The list and detail pages separate current reports from historical sign-ins. A reported copy does not mean a computer is online.
- **Key and app pages show their webhooks and delivery history.** The owner can retry a retained failed delivery, subject to the subscription's current settings and its maker's access. Creating or changing a subscription still needs its maker's credential.
- **Activity and types retain names.** Audit entries keep the acting sign-in's name at the time of the action. Type details identify the creator where one is recorded; unknown history stays unknown. The CLI's audit command also filters by kind and text.
- **More settings take effect without a restart.** General can change version retention, unused-app retirement, file reading and OCR, error reporting and heartbeat reporting. Receiver addresses and credentials remain machine settings.
- **Home and the CLI share Needs attention.** Health, failed jobs, connector and folder problems, and keys nearing expiry come from one operation. A failed check appears as unavailable alongside any known concerns.
- **Profile accepts a picture address.** The owner can set or clear an HTTP(S) picture URL in Profile or through the CLI's owner update command.

For running an instance and using the CLI:

- **An unresponsive file store no longer holds work indefinitely.** Blob reads try another recorded copy when a store does not begin answering. Integrity and replication jobs report work they could not finish. An unfinished store write or deletion blocks conflicting operations on the same blob, and removing a copy confirms enough other copies still exist.
- **Private-socket access excludes item content.** Item properties, edges, tags, search, counts, events and exports need a key. Summaries and owner trash operations remain available through the socket. A refusal explains which access to use.
- **Sign-in and command errors give the relevant next step.** Login keeps polling through temporary server failures while its code remains valid. Connected apps distinguishes the Marfa CLI from other apps using device authorization. Setup-code refusals, an unreadable keychain, an unreachable server and webhook retry refusals carry more specific errors.

For folders and app builders:

- **A folder push recovers once from a changed key's read view.** It rebuilds the copy and continues waiting writes in the same run, keeping earlier verdicts. A watch preserves the server's requested wait when it hands a folder to a command and takes it back.
- **Folder reports distinguish waiting, unchanged and held files.** First-sync plans count files they leave in place separately from files written beside existing ones. A document the scan refuses is held with its reason, rather than described as waiting to send.
- **A frontmatter type value can belong to another notes app.** A value that is not a Marfa type identifier stays in the item's `type` property and survives writing the file back. A value shaped like a Marfa type identifier still selects the item's type and is refused if it cannot be used.
- **The core's Swift binding accepts a credential renewer.** Apps can provide renewal for working copies and folders when a token is refused, with queued writes retained on failure. This is a binding change in Marfa; the Swift SDK has its own release.

## v0.0.20

Expand Down
6 changes: 4 additions & 2 deletions content/docs/(reference)/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,13 @@ That's how the owner makes the first key in the [Quickstart](/quickstart). These
| --- | --- | --- |
| `marfa setup status`, `setup code`, `setup open` | Check whether the instance is claimed, replace the setup code, and open the browser handoff. | [Quickstart](/quickstart) |
| `marfa owner recover` | Set a new owner password. | [Sign in as the owner](/guides/sign-in-as-the-owner#recover-the-owners-password) |
| `marfa owner update` | Change the owner's email and name. | [Sign in as the owner](/guides/sign-in-as-the-owner#change-the-owners-profile) |
| `marfa owner update` | Change the owner's email, name and picture address. | [Sign in as the owner](/guides/sign-in-as-the-owner#change-the-owners-profile) |
| `marfa owner passkeys` | Add, list, rename and remove passkeys. | [Sign in as the owner](/guides/sign-in-as-the-owner#manage-passkeys-and-sign-ins) |
| `marfa items summaries` | List item summaries without opening item content. | [Access](/access#who-can-act) |
| `marfa items trash` | List, move, restore and purge items in the trash. | [Housekeeping](/guides/housekeeping#empty-the-bin) |
| `marfa sign-ins` | List, rename and end the sign-ins that can reach the server. | [Sign in as the owner](/guides/sign-in-as-the-owner#manage-passkeys-and-sign-ins) |

**The socket carries no key.** `--socket` can't be combined with `--url`, `--key`, `MARFA_API_URL` or `MARFA_API_KEY`. An operation that needs a key, such as `marfa events` or `marfa export`, ends with exit code 5, so run it with a key instead. `marfa login`, `logout`, `device`, `folders`, `docs` and `types scaffold` refuse `--socket`.
**The socket carries no key.** `--socket` can't be combined with `--url`, `--key`, `MARFA_API_URL` or `MARFA_API_KEY`. An operation that needs a key, such as `marfa events` or `marfa export`, ends with exit code 5, so run it with a key instead. `marfa login`, `logout`, `device`, `docs`, `types scaffold` and local folder commands refuse `--socket`. `marfa folders inventory` is the exception: it reads the owner’s reported folder copies through the socket.

## Exit codes

Expand Down
10 changes: 6 additions & 4 deletions content/docs/guides/audit-log.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ Each entry holds these fields:
| --- | --- | --- |
| `id` | `string` | The entry's identifier. |
| `created_at` | `string` | When Marfa recorded the entry, in UTC. |
| `key_id` | `string \| null` | Who acted: the ID of a key or an app's token, or null for a write of Marfa's own, such as a housekeeping job. |
| `actor` | `object \| null` | The recorded sign-in kind, ID and readable name; Marfa for server work, or null when the actor is unknown. |
| `key_id` | `string \| null` | The acting credential's ID, or null when no credential is recorded. |
| `action` | `string` | What happened, such as `item.create` or `export.run`. |
| `resource_type` | `string` | The kind of thing acted on, such as `item`, `key` or `config`. |
| `resource_id` | `string \| null` | The ID of the thing acted on, or null where there is none. |
| `client_ip` | `string \| null` | The address the request came from, or null where there is none. |
| `details` | `object` | More about the action, depending on action. |

When the owner acts, `key_id` names the owner's authority rather than a key: `local:process` through the private socket, or `owner:` and the browser session when signed in. Behind a proxy or a tunnel, `client_ip` is the proxy's address unless the server is told which address to believe, as [Deploy](/guides/deploy#the-address) explains.
**Use `actor` to identify who acted.** For authenticated actions, its `kind`, `id` and `name` are captured at the time, so a later rename or removal does not change the entry. Server work has actor `{ "kind": "server", "id": "server", "name": "Marfa" }`. Older entries without a captured actor, and actions before authentication, have `actor: null`; do not infer a person from their `key_id`. Behind a proxy or a tunnel, `client_ip` is the proxy's address unless the server is told which address to believe, as [Deploy](/guides/deploy#the-address) explains.

## Read the log

Expand All @@ -58,6 +59,7 @@ marfa audit --action item.create --limit 1 --json
{
"id": "01a16970-ceb8-7dc2-9619-9b8d17b41c1b",
"created_at": "2026-10-23T15:46:03.066Z",
"actor": { "kind": "key", "id": "01a15f23-e1dd-7fac-850a-4f4f11a24b8a", "name": "Example key" },
"key_id": "01a15f23-e1dd-7fac-850a-4f4f11a24b8a",
"action": "item.create",
"resource_type": "item",
Expand Down Expand Up @@ -116,9 +118,9 @@ marfa audit --resource-type item --resource-id 01a16970-ceb9-7a31-a880-857c997d1

The HTTP request is `GET /audit?resource_type=item&resource_id=<id>`.

`marfa audit` takes the first five of these and not `kind` or `q`.
`marfa audit` takes all seven filters. Use `--kind` for `kind` and `--query` for `q`. [The audit kinds](https://github.com/withmarfa/marfa/blob/main/conformance/spec/events.md#eventsaudit-kind) and [the search](https://github.com/withmarfa/marfa/blob/main/conformance/spec/events.md#eventsaudit-search) hold the rules.

**There is no filter for `key_id`**, so to see what one key did, read the entries and compare their `key_id`.
**There is no filter for `key_id`**, so to see what one key did, read the entries and compare their `key_id`. Use `actor` for the readable identity where it was recorded.

A page holds 50 entries unless `limit` asks for up to 200. When more match, the answer carries a `next_cursor`: send it back as `cursor` (or `--cursor`) for the next page, and stop when it's `null`. **Marfa refuses a query key it doesn't know** with `400 validation_error`, so a misspelled filter never reads as one that matched everything.

Expand Down
4 changes: 2 additions & 2 deletions content/docs/guides/authenticate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Content-Type: application/json

Marfa answers `201` with the `client_id`. With `"token_endpoint_auth_method": "none"` the app proves itself with PKCE alone. Leave it out, and the answer also holds a `client_secret` to send with each token request.

Redirect addresses must use `https`, unless you also set `"application_type": "native"`, which allows an `http` loopback address such as `http://127.0.0.1:9000/callback`. [Register an OAuth client](/api-reference/access/registerOAuthClient) lists every field.
Redirect addresses must use `https`, unless you also set `"application_type": "native"`, which also allows an `http` loopback address or a reverse-domain private-use scheme with a path and no authority, such as `com.example.app:/callback`. [Register an OAuth client](/api-reference/access/registerOAuthClient) lists every field.

</Step>
<Step>
Expand Down Expand Up @@ -217,7 +217,7 @@ token=marfa_rt_…&token_type_hint=refresh_token&client_id=<client_id>

Marfa answers `200`, even for a token it doesn't hold.

The owner can also disconnect an app on the **Connected apps** page, and revoke the keys the app made at the same time. A credential holding `grants.manage` lists apps with `GET /auth/grants` and ends one with `DELETE /auth/grants/{id}`.
The owner can narrow or disconnect an app on the **Connected apps** page. Narrowing ends tokens and refresh chains that exceed the remaining access; covered chains keep working. Increasing access requires fresh approval. When disconnecting, the owner can revoke the keys the app made at the same time. A credential holding `grants.manage` lists apps with `GET /auth/grants` and ends one with `DELETE /auth/grants/{id}`.

Disconnecting from a browser **needs a sign-in from the last five minutes**, and the page asks the owner to [confirm it's them](/guides/sign-in-as-the-owner#confirm-its-you) if theirs is older. A program that sends the same request gets `403 forbidden`, and the app keeps working. A credential holding `grants.manage`, and the private local command, are not held to it.

Expand Down
2 changes: 1 addition & 1 deletion content/docs/guides/back-up-and-restore.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Back up and restore
description: Copy an instance's data directory and secrets so you can bring it back after a failed disk or a lost machine.
---

Keep a copy of your instance somewhere the instance isn't. A copy of the data directory brings the same instance back with everything in it: items, keys, configuration and sign-ins. To carry your data into another instance instead, such as a new build, [export an archive](/guides/export-and-restore).
Keep a copy of your instance somewhere the instance isn't. A copy of the data directory brings the same instance back with everything in it: items, keys, configuration and sign-ins. To carry your data into another instance instead, [export an archive](/guides/export-and-restore). A different build may refuse the archive; [Upgrade](/guides/upgrade) explains how to test it before switching.

## What to copy

Expand Down
Loading
Loading