SQLite
SQLite stores the board in one database on your server. The existing API reads cards and saves moves, fields, descriptions, criteria, comments and artifacts. Each write creates an immutable revision with its actor and message. No Git checkout is required. Choose authentication separately: the normal GitHub login works with SQLite, and token access remains an option for your own installation.
Compare storage methods for advantages, tradeoffs and the available CLI, skills and API tools.
Use GitHub login with SQLite and Git sources
One login can open both GitHub repositories and configured SQLite databases.
GitHub sources use repository permissions; databases use their own access list.
In the server’s agents.yaml:
board:
auth:
provider: github
server:
bind: "0.0.0.0:4350"
origins: ["https://board.example.com"]
hosted:
public_url: "https://api.example.com"
github_client_id: "YOUR_GITHUB_CLIENT_ID"
sources:
- id: roadmap
title: Roadmap
store:
kind: sqlite
path: /var/data/boards/roadmap.sqlite
access:
readers: ["github:1234"]
writers: ["github:5678"]Set FOLIO_BOARD_GITHUB_CLIENT_SECRET and FOLIO_BOARD_SESSION_KEY as in
Authentication. The client uses
kanban.api: {url: "https://api.example.com", auth: github}. Sign in normally,
then add the database in Profile → Settings → Sources. Repository sources
and SQLite cards share the same canvas and filters.
Access entries use stable provider subjects, such as github:1234, rather than
changeable usernames. Find your GitHub id with gh api user --jq .id.
Writers can also read. Access defaults to nobody; "*" explicitly allows every
signed-in account in that list. Each read and write checks the source’s current
permissions. Database writes record the signed-in person’s identity. Database
paths stay on the server; clients select the stable source id.
Add more database entries to board.sources. An optional seed: board imports
that source’s Markdown once. Neither cards nor their revisions are sent to
GitHub merely because GitHub provides the login.
Configure standalone token access
In the server’s agents.yaml:
board:
auth:
provider: token
store:
kind: sqlite
path: data/board.sqlite
server:
bind: "127.0.0.1:4350"
origins: ["http://localhost:3000"]The database path is relative to the project directory; an absolute path also
works. A new database starts with empty Ideas, Todo, In progress, In review and
Done columns. Add board.source: board to import an existing Markdown board
once. That folder needs board.yaml and its cards; it does not need Git.
Hidden files are excluded and symlinks are refused. On later starts the
database is authoritative and the seed folder is not read.
folio-agents board and the board skills still operate Markdown files; they
do not edit this database. The import is one-time, so later CLI edits to the
seed folder do not change SQLite. Use the browser or HTTP API after importing.
Start the server:
folio-board-server api --project-dir .Local mode prints a bearer token. In the site’s docs.yaml, select token
authentication explicitly:
kanban:
source: fixtures/board/board
routes:
public: "/board"
api:
url: "http://127.0.0.1:4350"
auth: tokenBuild the page with folio-agents build. Connect to your board accepts the
token. The page shows cards only after an authorized backend read. The token
stays in the tab’s session storage, scoped to the API address. It is never
placed in a query string or saved with browser preferences. Cards identify
their database source, so the existing source: and project: filters work.
Create cards directly through the SQLite API, including on an empty database:
curl -X POST http://127.0.0.1:4350/v1/cards \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
--data '{"id":"first-idea","markdown":"---\ntitle: First idea\nstatus: ideas\n---\n"}'The id is a lowercase slug. The server validates the Markdown and board relationships, refuses duplicate ids, and creates one atomic revision.
Run it behind HTTPS
Use a reverse proxy with TLS and configure the server’s public address:
board:
auth:
provider: token
store:
kind: sqlite
path: /var/lib/folio/board.sqlite
token_env: FOLIO_BOARD_TOKEN
server:
bind: "0.0.0.0:4350"
origins: ["https://board.example.com"]
hosted:
public_url: "https://api.example.com"Set FOLIO_BOARD_TOKEN in the server environment to a random hex token of at
least 32 characters. The server requires it for HTTPS mode and does not print
it. --token can also supply it. The site’s kanban.api.url becomes
https://api.example.com; keep auth: token. No OAuth client id or GitHub
secret is needed. Set the browser origin exactly, without a trailing slash.
Standalone token mode serves one database workspace. The bearer grants access
to that workspace; writes use the SQLite service identity. board.server.read_only: true opens an initialized database without allowing changes. The API checks
the configured host, the allowed browser origins and the bearer independently.
Keep the database on persistent storage
Mount a persistent volume at the database directory. Container rebuilds and server restarts reopen the same file. Hosting with an ephemeral filesystem does not preserve the board across redeployments.
SQLite uses WAL journaling, a busy timeout and transactions for writes. A revision and all its file changes commit together. A stale revision or file hash refuses the entire write; the normal API conflict response applies. Unchanged file contents are shared between revisions.
Use SQLite’s online backup mechanism for backups while the server is running. Keep the database and its WAL files on the same persistent volume.
Filters, named filters, appearance and layout remain browser preferences. The database persists board content and revision history; it does not sync personal settings between devices. Backend synchronization appears in Settings.
Deploy the database with its backend
Render and Docker keeps SQLite and authorization on persistent storage, serves MCP and includes the remote CLI. Render uses a paid disk; Docker Compose uses a named volume. API tokens connect tools without replacing the normal web login.