# Notes

A note is a collaborative Markdown document. People edit it in the browser in
real time; `dreamlake notes` is how a script or an agent reads and edits the
same document from a shell.

The commands are pipe-friendly on purpose: `read` writes the body to stdout and
nothing else, `write` takes text from a file or stdin, and `--json` is there
wherever the readable form would be awkward to parse.

## Finding a note

```bash file="terminal"
dreamlake notes list
dreamlake notes list --shared        # shared with you, from other namespaces
dreamlake notes search deploy
```

### Which namespace

Notes belong to a namespace, and **the default is your personal one** — not an
organization you belong to. An organization's notes live in its own namespace
and are only reachable by naming it:

```bash file="terminal"
dreamlake notes list                        # your own
dreamlake org list                          # the organizations you belong to
dreamlake notes list --namespace acme       # one of theirs
```

`--namespace` works on every command below. `notes list --shared` is the one
exception that crosses namespaces: it lists what other people shared with you,
wherever it lives.

A note is named by its slug, its title, or its id. All three work wherever
`<note>` appears below.

`search` is current: before querying, it flushes the notes anyone has open in
this namespace, so a sentence a colleague typed seconds ago is findable. You
do not have to wait for anything.

`search` also says **where** in each note it matched:

```
NOTE      SLUG        UPDATED     ID
Runbook   runbook     2026-03-01  507f…

runbook
  deploy  …Run `pnpm run deploy` to ship…
```

Go straight to that section — `notes read runbook --section deploy` — instead
of reading the whole note to find it. The most specific section is listed
first.

`search` matches **titles and bodies**, case-insensitively, by substring — so a
phrase inside a note finds it, and so does a fragment of an identifier like
`LAKE_REMOTE`. Chinese and other non-spaced scripts match the same way.

A note last written before bodies were indexed matches on its title only,
until someone edits it or an administrator runs the one-off backfill.

## Creating

```bash file="terminal"
dreamlake notes create "Design Doc"
dreamlake notes create "Design Doc" --file draft.md
dreamlake notes create "Public Notes" --text '# Hello\n' --public
```

Titles may repeat; the slug gets a suffix to stay unique, so the command
prints the slug and id it actually made rather than the title you asked for.

## Reading

```bash file="terminal"
dreamlake notes read design-doc                 # the whole body, to stdout
dreamlake notes read design-doc > local.md      # …which means this works
dreamlake notes sections design-doc             # the outline
dreamlake notes read design-doc --section install
```

`sections` lists what you can address:

```
ANCHOR    LEVEL  TITLE        CHARS
title     1      Design Doc     820
install   2        Install      412
macos     3          macOS      180
usage     2        Usage        228
```

A **section** is a heading plus everything under it, up to the next heading of
the same or a higher level — so `install` contains `macos`. The anchor is a
slug of the title, with a numeric suffix when titles repeat (`setup`,
`setup-2`). Text before the first heading is addressed as `preamble`.

## Writing

```bash file="terminal"
# one section
dreamlake notes write design-doc --section install --file install.md

# the whole body
dreamlake notes write design-doc --file whole.md

# inline, or from a pipe
dreamlake notes write design-doc --section install --text '## Install
pip install dreamlake
'
cat install.md | dreamlake notes write design-doc --section install

# add to the end
dreamlake notes append design-doc --text $'\n## Changelog\n- shipped\n'
```

A section is replaced **verbatim, heading included** — which is how you rename
one. Leave the heading out and the section stops being a section.

### Adding and removing sections

```bash file="terminal"
dreamlake notes insert design-doc --after install --text '## Troubleshooting

Check the logs.
'
dreamlake notes insert design-doc --before install --file prereqs.md
dreamlake notes insert design-doc --text '## Licence\n\nMIT\n'   # at the end

dreamlake notes rm-section design-doc troubleshooting
```

`--after` places the new section past that one **and its subsections** —
anything else would drop it inside the section you named. The heading is part
of the text, so you pick the level: a `###` can go under a `##`.

`insert` prints the new outline, because the anchor is only knowable afterwards
— a duplicate title takes the next free suffix.

`rm-section` removes the subtree too. That is what the section is; leaving the
subsections behind would promote them into the previous one.

## Writing while other people are in the note

Every write is a **real-time collaborative edit**. The server joins the note's
collaboration room and applies your change there, so anyone with the note open
watches it appear — and it merges with what they are typing, the same way two
people's edits merge.

That is true of all of them: `write`, `append`, `patch`, `add-section`,
`rm-section`. You do not have to wait for people to leave, and nothing is
locked.

### The precondition is a different thing

Collaboration handles two edits arriving at once. It does not help with an
edit built from a document that has since changed — read a note, spend a
minute deciding, write the whole body back, and you would erase what happened
while you were deciding.

So a write also sends the version it was based on. If the note moved in
between, it is **refused** rather than applied:

| Exit | Means | Do |
|---|---|---|
| `3` | The note changed since you read it | Re-read, redo the edit. Retrying as-is fails again. |
| `4` | The realtime service could not take the write, and people are editing | Transient — wait a few seconds and retry. |
| `5` | Your diff no longer applies | Re-read and regenerate it. |

Exit `4` is an infrastructure signal, not a queue. It means the collaboration
room was unreachable AND somebody is connected — the fallback (writing the
archive) would reset the room and cost them whatever they have not saved, so
the command refuses instead. With the service healthy you will not see it.

```bash file="terminal"
dreamlake notes write design-doc --section install --file new.md
case $? in
  0) echo "done" ;;
  3) echo "someone edited it — re-read and redo" ;;
  4) sleep 30; echo "retrying" ;;
  5) echo "regenerate the diff" ;;
esac
```

### Pinning a version yourself

`read --json` gives you the validator, which you can hold across a longer edit:

```bash file="terminal"
ETAG=$(dreamlake notes read design-doc --json | jq -r .etag)
# …edit…
dreamlake notes write design-doc --file new.md --if-match "$ETAG"
```

### Overwriting on purpose

```bash file="terminal"
dreamlake notes write design-doc --file whole.md --force
```

`--force` is the only way past the check. Overwriting a colleague should be
something you typed, not something that happened.

## Patching

A unified diff carries its own precondition — the context has to match — so a
document that moved refuses the patch instead of taking half of it.

```bash file="terminal"
dreamlake notes read design-doc > before.md
cp before.md after.md
# …edit after.md…
diff -u before.md after.md | dreamlake notes patch design-doc --file -
```

## Permissions

Reading needs read access; writing needs write access. A read-only share link
gives the first and not the second — reads work, writes fail with "read-only
access to this note". A note you cannot read at all reports as not found.

## Command summary

| Command | Does |
|---|---|
| `notes create <name>` | Make a note, optionally with a body |
| `notes list [--shared]` | Notes in the namespace, or shared with you |
| `notes search <query>` | Match note titles and bodies |
| `notes sections <note>` | The outline, with anchors |
| `notes read <note> [--section <anchor>]` | Body or one section, to stdout |
| `notes write <note> [--section <anchor>]` | Replace body or section |
| `notes insert <note> [--before\|--after]` | Add a section |
| `notes rm-section <note> <anchor>` | Remove a section and its subtree |
| `notes patch <note>` | Apply a unified diff |
| `notes append <note>` | Add to the end |

Every one of them takes `--namespace`, `--json`, and the usual connection flags.
`write`, `patch` and `append` take `--if-match` and `--force`.
