diff --git a/content/docs/(reference)/folder-sync-status.mdx b/content/docs/(reference)/folder-sync-status.mdx index 2e6fe8be..1f1ea8a6 100644 --- a/content/docs/(reference)/folder-sync-status.mdx +++ b/content/docs/(reference)/folder-sync-status.mdx @@ -27,6 +27,7 @@ The file sits beside the folder registry, `folders.json`: "processes": [ { "pid": 4821, + "build_version": "0.0.56", "started_at": "2026-10-15T09:00:00.000Z", "heartbeat_at": "2026-10-15T09:20:35.120Z", "heartbeat_interval_seconds": 5, @@ -55,6 +56,7 @@ The file sits beside the folder registry, `folders.json`: | Field | Type | Description | | --- | --- | --- | | `pid` | `number` | The watch process. | +| `build_version` | `string` or absent | The version of the CLI build that runs the watch, as `marfa --version` prints it. One that differs from the installed CLI's still runs an earlier build. A build too old to write the field drops it from every entry when it writes the file. | | `started_at` | `string` | When the process started. | | `heartbeat_at` | `string` | When the process last wrote its entry. | | `heartbeat_interval_seconds` | `number` | How often a running process writes: at least this often. | diff --git a/content/docs/(start)/connectors.mdx b/content/docs/(start)/connectors.mdx index d2961649..1d0bf013 100644 --- a/content/docs/(start)/connectors.mdx +++ b/content/docs/(start)/connectors.mdx @@ -3,7 +3,9 @@ title: Connectors description: How a connector brings records from another service into Marfa, and what the instance keeps so it can stop and resume safely. --- -Much of your data starts somewhere else: tasks in Todoist, issues on GitHub, articles in an RSS feed. A **connector** is a program that reads such a service, its vendor, and writes what it finds to your instance as items. It can also carry changes made in Marfa back to the vendor. +Much of your data starts somewhere else: tasks in Todoist, issues on GitHub, articles in an RSS feed. A **connector** is a program outside the server that **registers with your instance**, most often to read such a service, its vendor, and write what it finds as items. It can also carry changes made in Marfa back to the vendor. + +The registration is what makes a program a connector. An app or a script that reads and writes with a key, or through a sign-in, but never registers isn't one, and needs nothing on this page. A Todoist connector, for example, runs where you choose and reaches your instance with a key of its own. The instance keeps what the connector needs to resume, and Todoist can also post webhooks to an inbound endpoint on the instance. @@ -13,7 +15,7 @@ A Todoist connector, for example, runs where you choose and reaches your instanc The server never runs a connector or calls a vendor. A connector is an ordinary program you run wherever suits you, on a schedule or all the time, and it uses the same API as any app. That keeps the server small, and your vendor credentials out of it. -A connector runs with a key made for it alone. The key's [source](/items-and-types#where-an-item-came-from) labels everything the connector writes, and with each record's `source_id`, its ID at the vendor, lets the connector update a record rather than write it twice. +A connector registers under a key made for it alone. The key's [source](/items-and-types#where-an-item-came-from) labels everything the connector writes, and with each record's `source_id`, its ID at the vendor, lets the connector update a record rather than write it twice. What a connector brings in belongs in the [feed](/items-and-types#library-and-feed). A connector names `feed` on each write, or its key makes `feed` its default; otherwise its records land in the library. A connector names the library only for a file that belongs to an item already there, such as a picture saved for an entry a person kept. diff --git a/content/docs/(start)/devices.mdx b/content/docs/(start)/devices.mdx index aae3904b..5afff317 100644 --- a/content/docs/(start)/devices.mdx +++ b/content/docs/(start)/devices.mdx @@ -13,7 +13,7 @@ A device chooses the **slice** it keeps: a list of types, a tier and the edge ty The device fills its copy once, by a **hydration** that reads every item in the slice, then keeps it current from the instance's [event stream](/events). Reads, [saved views](/build/read-a-view#read-a-view-from-a-working-copy) included, need no connection. -**While the copy fills, the device answers from what has arrived**, marking a list or search `partial`. A saved view's items, any edge read or write, and a read or write of an item that hasn't arrived are refused `hydration_incomplete`. Other writes wait in the queue until the copy is whole. +**While the copy fills, the device answers from what has arrived**, marking a list or search `partial`. A saved view's items, groups and search, any edge read or write, and a read or write of an item that hasn't arrived are refused `hydration_incomplete`. Other writes wait in the queue until the copy is whole. **A fill that stops partway resumes where it stopped**, unless something such as the slice has changed. diff --git a/content/docs/(start)/how-marfa-works.mdx b/content/docs/(start)/how-marfa-works.mdx index 7da1072a..3952dc4a 100644 --- a/content/docs/(start)/how-marfa-works.mdx +++ b/content/docs/(start)/how-marfa-works.mdx @@ -36,6 +36,6 @@ Every program that uses Marfa runs outside it and sends requests to its HTTP API - **The Swift SDK and the CLI** embed the **core**, a Rust engine that sends their requests and can keep a working copy of the data they need. A program that keeps a working copy is a **device**: it reads offline and sends its writes when it can reach the instance, as [Devices](/devices) explains. The Swift SDK also sends the operations only the instance can do, such as registering a type, straight to the API. - **The TypeScript SDK** sends each request straight to the API, and keeps no copy. - **AI agents**, such as Claude Code or ChatGPT, connect to the instance's **MCP server** at `/mcp`, or run the CLI with a key of their own, as [Agents, the CLI and MCP](/agents-cli-and-mcp) explains. -- **Connectors** are programs you run that bring in records from another service, such as Todoist, as [Connectors](/connectors) explains. +- **Connectors** are programs you run that register with the instance and bring in records from another service, such as Todoist, as [Connectors](/connectors) explains. diff --git a/content/docs/build/connector-pull.mdx b/content/docs/build/connector-pull.mdx index 18123c1b..62307189 100644 --- a/content/docs/build/connector-pull.mdx +++ b/content/docs/build/connector-pull.mdx @@ -5,7 +5,7 @@ description: Write a vendor's records into Marfa as items, write only what chang A connector pulls: it reads the vendor and writes what it finds as items. A two-way connector also carries changes made in Marfa back, as [Carry changes back](/build/connector-push) shows, and a one-way connector says which types it holds read only. -The operations below come in the order a run uses them, for a connector built directly on the API. [The connector kit](/build/connector) does all of this for you but [declaring read-only types](#declare-the-types-you-dont-carry-back). +The operations below come in the order a run uses them, for a connector built directly on the API. [The connector kit](/build/connector) does all of this for you, [read-only types](#declare-the-types-you-dont-carry-back) included. The examples follow a task manager connector with a key of its own under the source `tasks`, which writes the type `tasks.task`. In the TypeScript examples, `marfa` is a [TypeScript SDK](/typescript-sdk) client made with that key. @@ -134,7 +134,7 @@ await marfa.POST("/connectors", { }); ``` -- **Each registration replaces the list**, and one without it clears the list. Build it on every start from what the connector carries back with the settings it runs on. +- **Each registration replaces the list**, and one without it clears the list. Build it on every start from what the connector carries back with the settings it runs on. **The kit does this for you**: it lists every type the connector writes and doesn't carry back, so a settings change reaches the list when the connector restarts. - **An item reads `true` only while every connector** whose live key owns or claims its source lists its type and may write it. Once that stops, its next read says `false`. - **Marfa doesn't refuse an edit** to a read-only item. The flag says what the connector will do. - **The list is per type.** Leave a type out when you carry back any of its fields or items. diff --git a/content/docs/catalog/(types)/(core)/highlight.mdx b/content/docs/catalog/(types)/(core)/highlight.mdx index 93b25759..1bb64110 100644 --- a/content/docs/catalog/(types)/(core)/highlight.mdx +++ b/content/docs/catalog/(types)/(core)/highlight.mdx @@ -6,6 +6,10 @@ identifier: core.highlight src/generated/catalog/core.highlight.md +## Name + +A highlight's passage is its text, never its name. A highlight with no `title` is named after the item it's an [`excerpt-of`](/catalog/edges#excerpt-of), such as "Excerpt from Thinking, Fast and Slow", so several highlights from one book share a name. With no such item, the server names it `Untitled`. [Names and previews](/catalog/display-hints#names-and-previews) has the rule. + ## Example ```json diff --git a/content/docs/catalog/(types)/(core)/media.mdx b/content/docs/catalog/(types)/(core)/media.mdx index ac672bbf..8c5540e2 100644 --- a/content/docs/catalog/(types)/(core)/media.mdx +++ b/content/docs/catalog/(types)/(core)/media.mdx @@ -8,9 +8,9 @@ identifier: core.media ## Progress and rating -Every media type, games included, inherits two fields for what the owner thinks of a work: +Every media type, games included, inherits two fields for where you are with a work and what you think of it: -- **`progress`** is where the owner is with it, a [status field](/catalog/field-kinds#status-words) whose group a `status_group` filter reads on any media type. A series also holds `status`, a separate field that says whether it's still producing episodes. +- **`progress`** is a [status field](/catalog/field-kinds#status-words) whose group a `status_group` filter reads on any media type. A series also holds `status`, a separate field that says whether it's still producing episodes. - **`rating`** is a whole number from 1 to 5. A fraction, or a number outside the range, is refused `400 invalid_properties`. Marfa doesn't compare a game's `min_players` with its `max_players`. diff --git a/content/docs/catalog/connectors/readwise-reader.mdx b/content/docs/catalog/connectors/readwise-reader.mdx index 737f63fe..4e32bf34 100644 --- a/content/docs/catalog/connectors/readwise-reader.mdx +++ b/content/docs/catalog/connectors/readwise-reader.mdx @@ -22,7 +22,7 @@ Each run reads what changed since the last, with a five-minute overlap, and a fu | `readwise.highlight` | [Highlight](/catalog/highlight) | A highlight with its annotation and tags | | `readwise.file` | [File](/catalog/file) | Kept content, attached to its document or highlight | -The Readwise connector writes the same types, so one item can hold what both connectors know. An item Reader manages carries a Reader link, `:reader-document:` or `:reader-highlight:`, in `canonical_key`. A highlight connects to its document through `references`, and a file connects to what it belongs to through `attached-to`. +The Readwise connector writes the same types, so one item can hold what both connectors know. An item Reader manages carries a Reader link, `:reader-document:` or `:reader-highlight:`, in `canonical_key`. A highlight connects to its document through [`excerpt-of`](/catalog/edges#excerpt-of), and a file connects to what it belongs to through `attached-to`, so moving a document or highlight to the bin takes its files with it, and restoring it brings them back. ## What goes back @@ -88,4 +88,4 @@ Each run fetches at most 100 pictures and 200 MiB, from at most 25 documents, an ## Run it -[Run a connector](/run/run-a-connector) runs any connector. This one's key holds write on `readwise.source`, `readwise.highlight` and `readwise.file`, and on the `references` and `attached-to` edges, and its [README](https://github.com/withmarfa/marfa-connectors/blob/main/connectors/readwise-reader/README.md) lists what the key needs. Saving pictures needs no other permission. +[Run a connector](/run/run-a-connector) runs any connector. This one's key holds write on `readwise.source`, `readwise.highlight` and `readwise.file`, and on the `excerpt-of` and `attached-to` edges, and its [README](https://github.com/withmarfa/marfa-connectors/blob/main/connectors/readwise-reader/README.md) lists what the key needs. The connector refuses a key without write on `excerpt-of`, such as one that holds `references` in its place. Saving pictures needs no other permission. diff --git a/content/docs/catalog/connectors/readwise.mdx b/content/docs/catalog/connectors/readwise.mdx index d56e447e..62c18f73 100644 --- a/content/docs/catalog/connectors/readwise.mdx +++ b/content/docs/catalog/connectors/readwise.mdx @@ -19,7 +19,7 @@ The first run exports everything. Later runs ask for what changed since the last | `readwise.highlight` | [Highlight](/catalog/highlight) | A highlight with its annotation and tags | | `readwise.file` | [File](/catalog/file) | Content kept for a document that Readwise Reader manages | -Each highlight connects to its source through `references`. An item's `source_id` is `:source:` or `:highlight:`, where the account is your `READWISE_ACCOUNT`. Readwise's original timestamps, location kinds and colors are kept as properties. +Each highlight connects to its source through [`excerpt-of`](/catalog/edges#excerpt-of), and a file to what it belongs to through `attached-to`, so moving a source or highlight to the bin takes its files with it. An item's `source_id` is `:source:` or `:highlight:`, where the account is your `READWISE_ACCOUNT`. Readwise's original timestamps, location kinds and colors are kept as properties. Each highlight has a `title` and a `body`, which apps show as its title and text. The title is the passage's first paragraph as plain text, cut at a word boundary to at most 80 characters. A blank line ends a paragraph and a heading is a paragraph of its own, so lines with a single line break between them are joined. The title leaves out list and quotation markers at its start, a heading's permalink mark, and closes up the gap before a closing punctuation mark. A passage that holds only a picture is titled after its source, as in "Picture from Deep Work". The body is the whole passage as Markdown, pictures included. @@ -48,4 +48,4 @@ Nothing. The connector sends no write to Readwise, and moving an item to the bin ## Run it -[Run a connector](/run/run-a-connector) runs any connector. This one's key holds write on `readwise.source`, `readwise.highlight` and `readwise.file`, and on the `references` and `attached-to` edges, and its [README](https://github.com/withmarfa/marfa-connectors/blob/main/connectors/readwise/README.md) has the `marfa keys create` command. +[Run a connector](/run/run-a-connector) runs any connector. This one's key holds write on `readwise.source`, `readwise.highlight` and `readwise.file`, and on the `excerpt-of` and `attached-to` edges, and its [README](https://github.com/withmarfa/marfa-connectors/blob/main/connectors/readwise/README.md) has the `marfa keys create` command. The connector refuses a key without write on `excerpt-of`, such as one that holds `references` in its place. diff --git a/content/docs/catalog/connectors/rss.mdx b/content/docs/catalog/connectors/rss.mdx index 8e323ea0..b8f3b989 100644 --- a/content/docs/catalog/connectors/rss.mdx +++ b/content/docs/catalog/connectors/rss.mdx @@ -76,7 +76,7 @@ Each run fetches at most 100 pictures and 200 MiB, and starts no fetch after 10 ### When an entry leaves -When an entry is archived, in the bin or purged, the next run archives its pictures. When the entry is active again, the run after brings back those its text still points to. It brings back only pictures it archived, and never puts a picture in the bin or purges one. +**A picture goes to the bin with its entry**, and comes back when the entry is restored. When an entry is archived, or is gone, the next run archives its pictures. When the entry is active again, the run after brings back those its text still points to. It brings back only pictures it archived, and never puts a picture in the bin or purges one. A picture a person moved to the bin or purged is never saved again for that entry. A picture the text stops mentioning is kept. diff --git a/content/docs/run/install.mdx b/content/docs/run/install.mdx index 1297cecf..cb4fd76e 100644 --- a/content/docs/run/install.mdx +++ b/content/docs/run/install.mdx @@ -62,7 +62,7 @@ To give an AI agent access to this server, run `marfa docs build/ai-agents`. -The command shows each command that changes something as it runs it, and never prints a secret. Your output names your release where the sample shows ``, and your own folder, address and link. +The command shows each command that changes something as it runs, and never prints a secret. Your output shows your own release, folder, address and link. @@ -89,7 +89,7 @@ The way you reach the instance decides which caller address the server believes, The instance gets a Docker network of its own, named for the install (`marfa-net` unless you pass `--name`), and the command reads its range from Docker. The network is made once and kept when the container is replaced, so the range stays the one the setting names. The port is 8600, or the next free one, unless you pass `--port`. It is published on the loopback address only, so only a proxy or tunnel on the same machine reaches it. -**For `cloudflare` and `proxy`, point the tunnel or proxy at `http://localhost:` and the port, on the machine that holds the folder.** The command says so when it asks for the address, and its summary repeats it as **Point at**, with the machine's name beside the folder. A tunnel aimed at another port, or running on another machine, makes the address answer `502`. +**For `cloudflare` and `proxy`, point the tunnel or proxy at `http://localhost:` and the port, on the machine that holds the folder.** The summary repeats this as **Point at**, beside the machine's name. A tunnel aimed at another port, or running on another machine, makes the address answer `502`. For `cloudflare`, also turn off Cloudflare's Email Address Obfuscation, or the instance's pages show email addresses as `[email protected]`. [The address](/run/deploy#the-address) says where. @@ -102,12 +102,14 @@ The command keeps everything in one folder, `marfa-server` in your home director + - **`compose.yaml`** is the same for every install. It runs the release image with `restart: unless-stopped` and one volume. -- **`.env`** holds what Docker Compose reads and what the command remembers between runs: the install's name, the release, the port, how you reach the instance and whether it starts at login. It holds no secret. -- **`marfa.env`** holds the server's [settings](/run/settings), including the two secrets. Only you can read it. The command makes the secrets once and keeps them on every later run. If the data volume exists and `marfa.env` doesn't, it stops, because new secrets would make every key stop working. +- **`.env`** holds what Docker Compose reads and what the command remembers between runs: the install's name, release and port, how you reach the instance and whether it starts at login. It holds no secret. +- **`marfa.env`** holds the server's [settings](/run/settings), including the two secrets. Only you can read it. The command makes the secrets once and keeps them on every later run. If the data volume exists and `marfa.env` doesn't, it stops, since new secrets would stop every key working. +- **`.secrets-unconfirmed`** marks secrets that no install has succeeded with yet, such as after a failed first run. **Until one succeeds, every run repeats the warning to keep `marfa.env`.** It holds no secret, and the first install that succeeds removes it. The folder works without the CLI: `docker network create marfa-net` once, then `docker compose up -d` in the folder. @@ -118,9 +120,9 @@ Run `marfa server install` again with a flag to change a setting, such as `--acc | Command | What it does | | --- | --- | | `marfa server status` | Shows whether the container runs and answers, the release, the address and whether the instance has an owner. | -| `marfa server upgrade` | Moves to the release of your CLI, or to `--release`. A release with a newer schema copies the database beside it and updates it in place, and the command prints where the copy is. It refuses an older release than the one installed, which doesn't know what a newer one changed in the database. See [Upgrade](/run/upgrade). | +| `marfa server upgrade` | Moves to the release of your CLI, or to `--release`. A release with a newer schema copies the database beside it and updates it in place, and the command prints where the copy is. It refuses a release older than the one installed. See [Upgrade](/run/upgrade). | | `marfa server start` | Starts Docker's engine on a Mac if it's down, then the container. | -| `marfa server uninstall` | Removes the container and keeps the data. `--delete-data` removes the volume, the network and the settings too, after you type the install's name (`--yes` skips the question). | +| `marfa server uninstall` | Removes the container and keeps the data. `--delete-data` removes the volume, network and settings too, after you type the install's name (`--yes` skips the question). | ## Start at login or boot @@ -128,7 +130,7 @@ The restart policy brings the container back whenever Docker's engine starts. ** ## Keep the settings elsewhere -If a secrets manager writes `marfa.env`, pass `--external-settings`. The command then never makes or changes the file. It checks that the file holds both secrets, an address the server accepts and a proxy setting that fits the way you reach the instance, and names what's missing without printing a value. It reads a quoted value, such as the `'https://marfa.example.com'` a secrets renderer writes, the way Docker Compose reads it. Run the command again after the file changes: Docker Compose replaces the container when the file's content changes. +If a secrets manager writes `marfa.env`, pass `--external-settings`. The command then never makes or changes the file. It checks that the file holds both secrets, an address the server accepts and a proxy setting that fits the way you reach the instance, and names what's missing without printing a value. It reads a quoted value, such as `'https://marfa.example.com'`, as Docker Compose does. Run the command again after the file changes: Docker Compose replaces the container when the file's content changes. ## Keep a copy in a bucket diff --git a/content/docs/run/keep-folders-running.mdx b/content/docs/run/keep-folders-running.mdx index 1e7aa455..3ced6a4b 100644 --- a/content/docs/run/keep-folders-running.mdx +++ b/content/docs/run/keep-folders-running.mdx @@ -26,13 +26,14 @@ watching every folder /Users/sam/Library/Application Support/Marfa/folders.json -Press Ctrl-C to stop it. `SIGTERM` stops it the same way, with exit 0, so a service manager that stopped it doesn't start it again. +Press Ctrl-C to stop it. `SIGTERM` stops it the same way, with exit 0, so a service manager that stopped it doesn't start it again. **A watch you started yourself keeps running the build it started with:** when you install a new one, it says so once and goes on, so start it again to use the new build. | Option | What it does | | --- | --- | | `--json` | Prints each line as one JSON object with a `dir` field. | | `--for ` | Stops the watch after that long. | | `--log ` | Writes its output to a file in place of the terminal. When Ctrl-C or `SIGTERM` stops the watch, the last line in the file names the signal and the time, and the process that sent it where your system reports one. | +| `--exit-on-new-build` | Ends the watch with exit status 3 once a new build is installed at the path it was started by, after the passes under way finish, so a service manager starts the new build. [Sync from login](/run/sync-from-login#after-you-upgrade-the-cli) uses it. | | `--key-file ` | Takes the server and key from a file in place of the keychain. See [Use a key file instead of the keychain](/run/sync-from-login#use-a-key-file-instead-of-the-keychain). | To watch one folder, name its directory: `marfa folders watch Notes`. diff --git a/content/docs/run/sync-from-login.mdx b/content/docs/run/sync-from-login.mdx index 9eb5eb79..06b9728e 100644 --- a/content/docs/run/sync-from-login.mdx +++ b/content/docs/run/sync-from-login.mdx @@ -11,7 +11,7 @@ description: Start the folder watch each time you sign in with marfa folders aut marfa folders autostart on ``` -It starts the watch at once and says which file it wrote. Running it again changes nothing unless the agent would differ, such as after the program moved or after an upgrade changed what the command writes. +It starts the watch at once and says which file it wrote. The agent runs `marfa folders watch --exit-on-new-build`, by the path you ran the CLI by, so **the watch moves to each new build you install**. Running `on` again changes nothing unless the agent would differ, such as after the program moved. - **The agent:** on macOS a launchd agent, and on Linux a systemd user unit. It holds **no credential and no server**: each folder uses the keychain's credential for its server, so on Linux the keychain must be unlocked at login. With a [key file](#use-a-key-file-instead-of-the-keychain) it holds that file's path and nothing from it. - **The log:** `folder-watch.log` beside the folder registry, readable by you alone. At 1 MiB it moves to `folder-watch.log.1`. @@ -24,7 +24,15 @@ It starts the watch at once and says which file it wrote. Running it again chang ## After you upgrade the CLI -**Run `marfa folders autostart on` again with the same options,** such as `--key-file`. An agent keeps running as it was written until `on` replaces it, so the settings a later build adds reach it only then. `status` says when the agent differs from what `on` writes now. +When an upgrade, such as `brew upgrade`, puts a new build at the path the agent names, **the watch moves to it by itself.** The watch notices it, finishes the passes under way and ends with exit status 3, and the service manager starts the new build. Nothing it was sending is lost: what it hadn't reached stays in the folder, and the new build sends it. The log names both builds: + +```text +restarting for a new build: marfa 0.0.55 to 0.0.56; the watch ends once the passes under way finish, and the service manager starts /opt/homebrew/bin/marfa +``` + +A path that leads to no file, or to a file that isn't a build of the CLI, such as during an uninstall, is said once in the log, and the watch goes on. + +**An agent that an earlier build wrote doesn't move by itself.** `status` reports it as outdated, and `marfa folders status` says when a folder's watch runs another build than the command. Run `marfa folders autostart on` again with the same options, such as `--key-file`, to replace it. The same holds whenever `status` says the agent differs from what `on` writes now. ## Check it or turn it off diff --git a/content/docs/run/troubleshooting/folders.mdx b/content/docs/run/troubleshooting/folders.mdx index 1690990a..1cdb6ee0 100644 --- a/content/docs/run/troubleshooting/folders.mdx +++ b/content/docs/run/troubleshooting/folders.mdx @@ -1,6 +1,6 @@ --- title: Folders -description: Fix a folder that refuses to open, whose store a newer CLI can't use, or whose new file is refused an edge its defaults ask for. +description: Fix a folder that refuses to open, whose store a newer CLI can't use, whose new file is refused an edge its defaults ask for, or whose attached file isn't written. --- A folder keeps its own working copy and its record of which file is which item. [Sync a folder](/run/sync-a-folder) sets one up. @@ -43,6 +43,16 @@ The push or scan that met the refusal says the file is sent and the folder's def The refusal also clears when the edge is accepted, or when the item goes to the bin ([default edge rules](https://github.com/withmarfa/marfa/blob/main/conformance/spec/folders.md#foldersdefault-edge-refused-held)). +## An attached file isn't written + +The server refused to show one file attached to a note, such as with `409` or `400`. **The pull writes every other file and leaves only that attachment out.** The pull, and `marfa folders status`, report the note it is attached to with the flag `attachment`, naming the file's item and the reason: + +```text +notes/trip.md: its attached file cannot be read (); it is written once it can be read +``` + +Each later pull asks for the file again, and writes it once the server shows it. Read the item with `marfa items get ` to see what the server answers, and fix that. A refused credential is different: it ends the pull, and [the CLI](/cli#connect-to-a-server) shows how to sign in again. A file that can't be fetched for want of a network isn't reported, and comes with a later pull ([attachment rules](https://github.com/withmarfa/marfa/blob/main/conformance/spec/folders.md#foldersattachment-unread-held)). + ## Covered in their guides | What you see | Guide |