Skip to contentSkip to Content
GuidesEdit the board in the browser

Edit the board in the browser

A published board is static by default: it shows the files as they were at build time, and a drag only stages a move. Three steps make the same page edit the board:

Name the server in docs.yaml

kanban.api in the site’s docs.yaml names the board server, and agents.yaml configures it. See Two keys turn the board live.

Run the server

folio-board-server api runs beside the checkout that holds the board.

Open the link it prints

The link carries a token new on every start. See The server prints the link that opens the board.

The server writes the card file, checks the whole board, and commits. The files in git stay the source of truth; the page reads them back after every save.

A live board: the filter bar shows the live mark

Two keys turn the board live

The site that renders the board names the server. For the server on this page, the URL is http://127.0.0.1:<port> or http://localhost:<port>, the two hosts it answers. An https:// origin with no path names a hosted server instead (board.server.hosted): the board then offers Sign in with GitHub and keeps the session in the browser for 8 hours. Any other value fails the build.

# docs.yaml kanban: source: board routes: public: true api: url: http://127.0.0.1:4350

The project whose agents.yaml names the board configures the server:

# agents.yaml board: source: board server: bind: 127.0.0.1:4350 # the default; loopback only origins: ["http://localhost:4340"] # the pages allowed to call it read_only: false # true refuses every write
KeyDefaultMeaning
board.server.bind127.0.0.1:4350The address the server listens on. Use 127.0.0.1: the server answers only 127.0.0.1 and localhost in Host.
board.server.origins[]Browser origins allowed to call the server, matched exactly. Empty, no page can call it.
board.server.read_onlyfalsetrue answers every write with 403 read_only.

A site built without kanban.api carries no server address and no editor code. Removing the key and rebuilding returns the page to the static board.

# REPOSITORY is Folio Board's git URL. The build fetches Folio Docs from its # public repository. CARGO_NET_GIT_FETCH_WITH_CLI=true cargo install --locked \ --git "$REPOSITORY" folio-board-server folio-board-server api --project-dir .
board: /home/ana/widgets/board api: http://127.0.0.1:4350 open: http://localhost:4340/kanban/#board-token=3f9c… token: 3f9c…

The token is a random 256-bit value, new on every start. Open the open: link: the page reads the token from the address, keeps it in the tab’s session storage, and removes it from the address bar. The part after # is never sent to a server. A new tab or a restarted server needs the link again. --token HEX fixes the value, for scripted demos.

The server never pushes

It commits in the board’s own checkout. Push the board branch as you would after folio-agents board move --commit.

Every save is one commit

A live card has no edit mode. Each value in the dialog is its own control: click it, change it, and press Enter or leave the field. The value shows at once with a mark while the commit runs; a refused save rolls back and says why under the control.

GestureWhat the card file getsCommit message
Drag a card to another columnstatus:board: <id> <from> -> <to>
Edit the title, milestone, track, type, tags, priority, size, assignee, parent, blocked by or createdthat frontmatter fieldboard: update <id>
Edit the descriptionthe body before the first machine headingboard: describe <id>
Tick or untick a criterionits checkboxboard: check criterion on <id>
Rewrite a criterionits textboard: update criterion on <id>
Add a criterionone open line under ## Acceptance criteriaboard: add criterion on <id>
Add a commentone line at the end of ## Commentsboard: comment on <id>
Add a trail noteone line at the end of ## Trailboard: trail <id>
Add an artifactone artifacts: entryboard: attach <kind> to <id>
Remove an artifactthat artifacts: entryboard: detach <kind> from <id>

Comments and trail lines carry today’s date and the server’s actor, the slugified git user.name of the board checkout. No request names an actor. A move shows Undo, which commits the move back.

The artifact tile takes a URL, #42 for a pull request, or the path of a file the board already carries, from the card’s directory or as board/cards/<id>/…. A file elsewhere in the project is refused; attach it with folio-agents board attach. Only entries listed under artifacts: can be removed; a file in the card’s directory stays until the file goes.

Adding an artifact on a live card

The description opens in a rich editor

Edit description opens the card’s description in place. Selecting text shows a bar with bold, italic, inline code, a link, two heading levels, two kinds of list and a quote; / at the start of a line inserts a block, such as a code block or a divider. Cmd or Ctrl+Enter saves. The server serves the editor itself, at /editor/v1.js, so a static site never loads it.

The description editor on a live card

The editor opens as plain Markdown source instead, with a line that says why, in three cases:

  • the description uses a component, such as <Tabs>;
  • the rich editor would rewrite part of the description;
  • the editor did not load.

Only the description reaches the editor. The frontmatter, the criteria, the comments and the trail keep their own controls, and the server refuses a description that would change them, such as one that adds a ## Trail heading. Pasted plain text joins its lines into one paragraph line, as board fmt writes them.

A card that changed is refused, not merged

Every edit except a comment, a trail note and an attached artifact carries the hash of the card the page last read. When the card file changed since, the save is refused with 412 precondition_failed and the card as it is now. The page shows the current card; make the edit again. A comment, a trail note and an attached artifact need no hash: each adds a dated line or an entry and changes no text the page showed. Adding a criterion needs the hash, like every other edit.

A commit to another card is not a conflict. The server re-reads the board and retries up to three times, then answers 409 stale.

Each save must leave a valid board, and a refused save writes nothing. A parent that makes a cycle gets 400 invalid_input. A description that would change another section, such as one that adds a ## Trail heading, gets 422 board_invalid.

Without the server the board stays static

When the server does not answer, or the tab has no token, the filter bar shows read-only. The board then works as a static board: moves are staged in the browser and Export moves prints the folio-agents board move commands.

The server answers loopback only

These rules hold for the local server. A hosted server answers its public host and takes a GitHub session instead; the Board API lists its routes.

  • It listens on a loopback address; any other bind is a config error.
  • A request whose Host is not 127.0.0.1:<port> or localhost:<port> gets 421 misdirected, which stops DNS rebinding.
  • Only the pages in origins pass CORS, and no cookies are used.
  • Every route except /editor/v1.js needs Authorization: Bearer <token>.
  • A request body is at most 256 KB.

What the browser cannot do

Not available in this release

Creating, deleting and archiving cards and editing board.yaml. Use folio-agents board add and a hand edit.

The routes the page calls are listed in the Board API.