In-Browser Editing

In-Browser Editing

mbr can let you edit markdown files directly from the browser while running in server or GUI mode. It is off by default and intended for private use (for example, the GUI window on your own machine). When enabled, a pencil button (✎) appears next to the info button; pressing it — or the e shortcut — opens a Milkdown/Crepe editor for the current file.

Editing writes to your files. Only enable it on repositories you intend to modify, and read the security model before exposing it beyond localhost.

Enabling editing

Enable it with a CLI flag:

mbr -s --edit ~/notes

or in .mbr/config.toml:

edit_enabled = true

On loopback (127.0.0.1), that’s all you need — local edits require no token (they are still CSRF- and Host-checked). Note that as soon as an edit_token_hash is configured it is required for every request, loopback included. For remote access, see Remote editing.

Using the editor

  1. Open a markdown page and click the ✎ button (top right) or press e.
  2. The editor loads the file. The body is edited in the WYSIWYG Crepe editor; any YAML frontmatter is shown in a separate raw-text field (open the “YAML frontmatter” section to edit it). Frontmatter is preserved verbatim — comments and key order are not reformatted.
  3. Click Save. On success the file is written and the page live-reloads.
  4. Press Esc or click outside the modal to close it.

Editing media

To embed an image, video, audio clip, or PDF, click Insert media in the footer and pick a file. To change an embed that’s already in the document, double-click it — the media picker reopens prefilled with its current file and caption so you can swap the file or edit the caption. You can also place the cursor on (or next to) an embed and use the footer button: when an embed is the current target it reads Edit media instead of Insert media.

Error handling

ResultMeaning
Authentication requiredA token is required (an edit_token_hash is configured, the caller is remote, or edit_require_token_on_loopback). Enter it and retry.
Editing is disabled or blockedEditing is off, or the request failed the CSRF/same-origin/Host check.
File changed on diskThe file was modified since you loaded it (e.g. by the watcher or another editor). Reload the page before saving.

Creating, renaming, and moving files

Beyond editing an existing file’s contents, the editor can also create new markdown files, create folders, and rename or move files. These operations are server/GUI-mode only and gated by the same access checks as editing (see Security model); the editor does not delete files.

File-management endpoints

All three require the same X-MBR-Edit: 1 header, same-origin, and (where applicable) token as /.mbr/edit. The {*path} segment is a repo-relative filesystem path with extension (e.g. docs/new.md) — the same convention as /.mbr/raw and /.mbr/edit — and each returns the computed url_path in its JSON response.

EndpointBodyPurpose
POST /.mbr/create/{*path}{ "content": "…", "create_dirs": false }Create a new markdown file (409 if it already exists).
POST /.mbr/move/{*path}{ "to": "docs/new.md", "create_dirs": false }Move/rename {*path} to to, rewriting links repo-wide (409 on collision).
POST /.mbr/mkdir/{*path}(none)Create a folder (idempotent).

Set create_dirs: true to create any missing parent directories at the destination; otherwise a missing parent is rejected with 400.

Task toggling

With editing on, task checkboxes become clickable: a left click completes or reopens a task and a right click cancels it, both in a rendered page and in the task browser (where Space and x do the same from the keyboard). Flipping one patches the single line it came from:

EndpointBodyPurpose
POST /.mbr/task{ "path": "docs/guide.md", "line": 42, "expected": "- [ ] write the report", "to": "done" }Set one task’s status to open, done or canceled.

It answers to edit_enabled, not to tasks_enabled — in-document checkboxes exist whether or not the task browser does — and it carries the same headers and token as every other write endpoint. On success it returns { "line": 42, "text": "…" } with the line’s new text.

expected is the line-sized version of /.mbr/edit’s base_hash: it must still match the file on disk (ignoring the line terminator) or the request is rejected with 409. Matching one line rather than the whole file means somebody else editing a different part of the document does not spuriously fail your click, while an edit to that line — the only case where flipping its marker could clobber something — still does.

Only the marker byte and the @done(...) annotation change; indentation, bullet style, spacing, other annotations, the line’s terminator (a CRLF file stays CRLF) and the presence or absence of a trailing newline are all preserved. See tasks_stamp_done for the stamp.

A toggle deliberately does not live-reload the page. The marker byte and the @done(...) stamp are the only things a status change alters, so the page updates itself from the response instead of re-rendering — which keeps your scroll position, keeps the task browser’s overlay and filters intact, and (on a token-protected server) keeps the in-memory token alive so the next click still authenticates. An edit made anywhere else still reloads the page as usual.

Generating a token

For remote editing you need a shared token. Generate one:

mbr --generate-edit-token

You’ll be prompted for a password (leave it blank to auto-generate a random token). It prints the token (give this to whoever edits) and an edit_token_hash = "..." line to paste into .mbr/config.toml. Nothing is written to disk for you, and only the Argon2 hash is stored — never the token itself.

# .mbr/config.toml
edit_enabled = true
edit_token_hash = "$argon2id$v=19$m=19456,t=2,p=1$...."

Entering the token

The editor’s footer has a token field. It is hidden until there is a reason for it, and revealed automatically when something is refused for want of a token — including a task checkbox, so “open the editor and enter it” always leads to a visible field.

The token is held in memory only, for as long as the page is open. It is never written to localStorage or sessionStorage: mbr renders arbitrary markdown and markdown may contain raw HTML, so a token parked in web storage would be a durable credential that any script on the page could read and replay later, long after you had finished editing. A variable dies with the page.

In practice that means one token entry per page load, and only on a token-protected server:

Remote editing

mbr serves plain HTTP with no built-in TLS. The editing token is sent with every request, so for any non-loopback use you should put mbr behind a TLS-terminating reverse proxy (nginx, Caddy, etc.). Startup validation refuses to enable editing on a non-loopback host (--host other than a loopback address) unless an edit_token_hash is configured.

Behind such a proxy every request reaches mbr from 127.0.0.1, so peer-address trust would silently disable authentication. A configured edit_token_hash is therefore enforced for all callers regardless of the peer address — you do not need edit_require_token_on_loopback to make the proxied deployment safe. Because the proxy presents its own public Host, the Host check below is skipped once a token is configured: the token is then the authority.

Security model

See the Configuration Reference for the full list of options and the CLI Reference for the flags.

Keyboard shortcuts

KeyAction
eOpen the editor for the current file (when editing is enabled)
EscClose the editor