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

# Vaults

> Obsidian compatible Markdown vaults, encrypted and versioned

A vault is a folder of Markdown notes that links with `[[wikilinks]]`, the same way an Obsidian
vault does. Vaults belong to a workspace, like the rest of [Context](https://docs.nemu.cc/context.md), so every member
shares them.

Each vault has its own encryption key, and its files are encrypted at rest. Every change is kept as
a new version, so an edit or a delete can be undone. `nemu sync` keeps a folder on your computer,
such as an Obsidian vault, in step with a vault (see [Syncing a folder](#syncing-a-folder)). An
Obsidian plugin is not available yet.

## In the console

Open **Context**, then **Vaults**, in the [console](https://app.nemu.cc/context?tab=vaults). The list
shows each vault's file count and storage, and its menu renames, exports, imports a zip into, or
deletes a vault. Deleting asks you to type the vault's name.

A vault opens on three views:

* **Notes.** A file tree with folders, a search box, and an editor with Edit and Preview. Preview
  turns `[[wikilinks]]` into links that open the note; a link to a note that does not exist yet is
  dashed, and selecting it creates the note. Save with the button or Ctrl S (⌘ S on a Mac). Each
  file's menu renames or moves it, or moves it to the trash; a folder's menu does the same for
  everything inside it. When you rename or move from the console, **Update links in other notes**
  (on by default) rewrites the `[[links]]` and Markdown links that pointed at the old name, the way
  Obsidian does. Each changed note gets a new version. A new folder is kept once a note is saved in
  it.
* **Graph.** Every note as a dot and every link as a line, with dashed circles for links to notes
  that do not exist yet. Drag to pan, scroll to zoom, hover to highlight a note's neighbours, and
  click to open it. The search above the graph finds a note by title or path.
* **Trash.** Deleted files, kept for 30 days and then removed with their history. Restore brings
  back the last saved version.

Beside the editor, **Links** lists the note's backlinks and outgoing links, and **History** lists
every version with its time and who saved it: the console, a device or an agent. Open a version to
read it, and restore it to make it the current version again.

If someone else saves a note while you are editing it, Save does not overwrite their change. The
console shows their version next to yours, and you choose to keep theirs or overwrite with yours.

When two devices change the same note before syncing, the other version is saved beside it as
`Note (conflict, <device>, <time>).md`. The console lists these open conflicts above the file tree.
Keep what you want in one copy and move the other to the trash; that resolves it on every device.

When `nemu sync` merges a note on its own, the merge is listed under **Merges** above the file tree,
with the time and the device. **Keep the merged text** accepts it. **Use the other device's version**
puts back the vault's text from before the merge, and **Use this device's version** puts back the
syncing computer's text from before the merge; either one is saved as a new version. A merge stays
listed for as long as your plan keeps history.

## Syncing a folder

The [CLI](https://docs.nemu.cc/context/cli.md) syncs a folder with a vault, from version 1.6.0. Sign in with `nemu login`
or `nemu login key`, then link the folder once:

```bash theme={null}
nemu vault link ~/Notes --vault "Notes"     # or --create to make a new vault
nemu sync ~/Notes                           # one run
nemu sync ~/Notes --watch                   # keep syncing until Ctrl C
```

Linking writes `.nemu/vault.json` in the folder. This file is how `nemu sync` knows the folder is
really there: without it, sync refuses to run, so an unmounted drive or a mistyped path never looks
like a folder whose notes were all deleted. What this computer last agreed with the vault is kept
in `~/.nemu/vaults/<vault id>.json`.

Each run downloads what changed since the last one, then compares every file with what it last
synced by its content, never by its modification time:

* A file changed on one side only is uploaded or downloaded. Downloads are written to a temporary
  file in the same folder and then renamed, so Obsidian never shows half a note.
* A file is uploaded only after it has been unchanged for 2 seconds and reads the same twice.
* A Markdown note changed on both sides is merged line by line when the edits do not touch the same
  lines. Line endings do not count as a change, and the merged note keeps your file's line endings.
  When the edits overlap, the vault's version keeps the name and yours is saved beside it as
  `Note (conflict, <device>, <date> <time>).md`, on every device. A note never gets conflict
  markers. Every automatic merge is recorded in the vault's merge log together with your text
  from before the merge, so either side can be restored from the console. If the merge log cannot
  be reached, the sync still finishes and says so.
* Any other file changed on both sides keeps both in the same way.
* An edit wins over a delete: a note deleted on one device and edited on another comes back.
* A rename is sent as a move, so the note keeps its history. When two devices rename different
  notes onto the same name, the second one gets ` (2)`.
* The same new file created on two devices is kept once.

`--conflicts=file` makes this device always write a conflict file instead of merging, and
`--conflicts=merge` turns merging back on. The choice is remembered per device. `--dry-run` lists
what a run would do and changes nothing.

Some things stop a run or a file on purpose:

* **Many deletes.** A run that would delete more than 10 files, or every file, or at least 3 files
  and more than 10% of the vault, stops before changing anything and says why. Run it again with
  `--allow-deletes` if you really deleted them.
* **Another sync on the same folder.** `.nemu/lock` holds the running process. A lock left by a
  process that is no longer running is taken over.
* **Skipped files.** Symbolic links, files above your plan's file size, two names that differ only
  by case, and names Windows cannot hold are skipped and listed, never uploaded.
* **Out of storage, or a paused workspace.** Uploads stop with a message and downloads continue.
  Nothing stored is removed.
* **A revoked device or key.** Sync stops at the next request and leaves every local file in place.

`.git/`, `node_modules/`, `.trash/`, `.DS_Store`, `._*` files and `.obsidian/workspace*.json` are
never synced. The rest of `.obsidian/` (themes, plugins, settings) is off by default because it is
often meant to differ per computer; `nemu vault link --obsidian` or `nemu sync --obsidian on` turns
it on for one device. A `.nemuignore` file in the folder adds patterns in `.gitignore` style.

`nemu vault status` shows the last sync, the device settings and the open conflict copies.
`nemu vault unlink` stops syncing the folder and keeps its files.

## Authentication

Base URL `https://app.nemu.cc/api/v1`. Send `Authorization: Bearer` with an
[API key](https://docs.nemu.cc/console/keys.md) with Context access or an OAuth token, exactly as for the
[REST API](https://docs.nemu.cc/context/api.md). Reads need Context read access and writes need Context write access.

## Vaults

| Method and path | Does |
| - | - |
| `GET /vaults` | List the vaults in the workspace |
| `POST /vaults` | Create one: `{ "name" }` |
| `GET`, `PATCH /vaults/{id}` | Read or rename a vault. `GET` also returns `workspace_id` and `limits`: `max_file_bytes` and `max_storage_bytes` (`null` when unlimited) |
| `DELETE /vaults/{id}` | Delete the vault and every version, permanently. The body must be `{ "confirm": "<vault name>" }` |

## Files

A file is addressed by its path inside the vault, passed as `?path=`.

| Method and path | Does |
| - | - |
| `GET /vaults/{id}/files?path=` | The raw content, with `ETag: "<version>"` |
| `PUT /vaults/{id}/files?path=` | Create or change a file; the body is the raw content |
| `DELETE /vaults/{id}/files?path=&version=` | Move the file to the trash |
| `POST /vaults/{id}/files/move` | Move or rename: `{ "from", "to", "base_version" }`, and optionally `"rewrite_links": true` |
| `GET /vaults/{id}/notes` | List the notes; `?trash=true` lists deleted ones |

A `PUT` must say which version it expects, so two writers never overwrite each other:

* `If-None-Match: *` creates a file that does not exist yet.
* `If-Match: "<version>"` changes the version you read.
* A stale version gets `409` with the current note. A request with neither header gets `428`.
* An optional `X-Nemu-Client` header names the writer in the note's history (letters, digits,
  spaces and `. _ : / @ ( ) -`, up to 64 characters). `nemu sync` sends its version and the
  computer's name.

```bash theme={null}
curl -s "https://app.nemu.cc/api/v1/vaults/$VAULT/files?path=ideas/queues.md" \
  -X PUT \
  -H "Authorization: Bearer $NEMU_API_KEY" \
  -H "If-None-Match: *" \
  --data-binary @queues.md
```

With `"rewrite_links": true`, a move also rewrites the links in other notes that pointed at the
note, and the answer adds `links_updated` (how many notes got a new version) and `links_failed`
(paths that could not be updated, for example because they changed at the same moment). Links keep
their `#heading`, `^block` and `|label` parts and stay embeds when they were; a link written as a
folder path gets the new path, a bare name gets the new name when it is unique and the path when it
is not, and a link through an alias is left alone, because the alias still works. It is off by
default, because Obsidian already rewrites links before `nemu sync` sends a rename.

Paths follow a few rules. A name Windows cannot hold, `..`, and a path over 240 characters are
refused. So is a path that differs only by case from one that already exists. One request uploads
at most 4 MB.

## Links, search and history

| Method and path | Does |
| - | - |
| `GET /vaults/{id}/changes?since=<revision>` | Notes changed after a revision, with the new `revision` |
| `GET /vaults/{id}/search?q=` | Search the notes |
| `GET /vaults/{id}/links?path=` | Outgoing links and backlinks of one note |
| `GET /vaults/{id}/graph` | Every note and link, for drawing a graph |
| `GET /vaults/{id}/history` | List the versions of a note; pass `note_id` |
| `POST /vaults/{id}/history` | Restore a version: `{ "note_id", "version" }` |
| `GET /vaults/{id}/history/version?note_id=&version=` | Read one old version |
| `GET /vaults/{id}/merges` | The merge log, newest first; `?open=true` for unresolved ones, `?note_id=` for one note |
| `POST /vaults/{id}/merges` | Record an automatic merge: `{ "path", "merged_version", "remote_version", "content", "client" }`, with this device's text before the merge as base64 in `content` |
| `POST /vaults/{id}/merges/{merge_id}` | Resolve one: `{ "keep": "merged" }`, `"remote"` or `"local"` |

A link resolves the way Obsidian resolves it: the exact path first, then the shortest path when
several notes share a name, then a note whose frontmatter `aliases` hold the name.

A recorded merge stores this device's text like any other file, so it counts toward the vault's
storage, and it is removed with the history once it is older than your plan's history window.
Recording the same `merged_version` twice returns the first record. `"remote"` and `"local"` save
that side as a new version of the note and answer `410` when the other device's version has already
left the history.

## Import and export

`GET /vaults/{id}/export` returns the vault as a zip. `POST /vaults/{id}/import` takes a zip as the
body, up to 4 MB. A file that already exists with different content is kept, and the imported one
is saved next to it as an "(imported ...)" copy. `.obsidian/` and other editor files are skipped.

## Limits

Vaults count against the workspace's plan, per workspace. These numbers may still change.

| | Free | Starter | Pro | Max | Enterprise |
| - | -: | -: | -: | -: | -: |
| Vaults | 1 | 5 | 20 | 50 | unlimited |
| Storage | 100 MB | 2 GB | 10 GB | 50 GB | unlimited |

See [Plans](https://docs.nemu.cc/plans.md) for every other limit.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.