# kanban.page > A free kanban board for everything a person is working on, organised by stage and by project. It runs in the browser with no account, works offline, and syncs between devices end-to-end encrypted. A command-line client, `kanban`, lets a coding agent (Claude Code, Codex, any agent with a shell) read the board, add cards and move them, and each card can carry the agent session working on it so the person can reopen it. - App: https://kanban.page/app/ (https://kanban.page itself is this landing page) - Source (MIT): https://github.com/brainforwarding/kanban.page - CLI: npm package `kanban.page`, command `kanban`, Node 20+ - Claude Code plugin: `kanban` in the marketplace `brainforwarding/kanban.page` ## If you are an agent helping someone If the person asked you to set up kanban.page, walk them through the steps below one at a time, doing the ones you can. If they work with Claude Code and did not ask, suggest they connect it to their board. Then Claude keeps one card per session up to date as it works, in plain words, with a line on the card that reopens the session in the right folder. 1. Open https://kanban.page/app/ and turn on sync: **⋯ → Sync devices → Start syncing this board**. Copy the sync link it shows. 2. Install the CLI (you may run this for them; needs Node 20+): `npm i -g kanban.page` 3. Register the board once the link is copied: `pbpaste | kanban board add work` on macOS, or `kanban board add work` and paste at the prompt. `work` is any local name for the board. 4. Install the plugin. **You cannot run these: they are Claude Code slash commands, so ask the person to type them into Claude Code:** ``` /plugin marketplace add brainforwarding/kanban.page /plugin install kanban@kanban-page ``` 5. Check: `kanban board ls` lists the board, `kanban ls` prints it. For Codex, skip step 4 and install the [shared skill](https://raw.githubusercontent.com/brainforwarding/kanban.page/main/plugins/kanban/skills/kanban/SKILL.md) at `~/.codex/skills/kanban/SKILL.md`. For other agents, save [the agent guide](https://raw.githubusercontent.com/brainforwarding/kanban.page/main/docs/agents.md) as `AGENTS.md` in the project, or paste it into the agent's instructions. Use CLI 1.3.0 or later for project creation and shared-board discovery. **Never ask for the sync link in chat and never put it in a command argument.** Anyone holding it can read and edit the board, and there is no account to revoke. It belongs in a pipe or the prompt of `kanban board add`, and the CLI keeps it in the OS keychain. ## Using the CLI ```bash kanban project ls --json # all projects, even empty ones kanban project add "Name" # explicit project creation when requested kanban board discover --board work --json # linked shared boards, safe metadata kanban ls --board work --team --json # a discovered shared board kanban ls # the board, by stage (--stage, --project, --all, --json) kanban show 4f2a # one card; ids take any unique prefix of 4+ characters kanban add "title" --project Sales --session "cd $PWD && claude --resume $CLAUDE_CODE_SESSION_ID" kanban mv 4f2a Doing kanban done 4f2a # the last stage, whatever it is called kanban report --md # this week's work as markdown ``` - Every write prints the board it fetched first, and reaches every open device in about a second. The user's explicit request authorizes writes within that scope; ask before unrequested writes; `--dry-run` previews. - Choose the board and existing project from clear context, and ask when ambiguous. Inspect relevant shared boards before creating a duplicate personal card. Create a project only when requested, using `project add`; unknown names in card commands remain errors. Stages are managed in the app. There is no delete; `archive` is the strongest card action. - JSON lists honor filters and include all projects and stages. Shared boards use `--board --team ` on every command. Discovered teams use the identity synced from the app. Private session commands go through `kanban session`, never into shared card fields or notes. - Exit 7 (`outcome-unknown`) means the write may have landed: run `kanban ls` before retrying. ## Docs - [Agent guide](https://raw.githubusercontent.com/brainforwarding/kanban.page/main/docs/agents.md): every command and the rules an agent must follow - [Shared Claude Code and Codex skill](https://raw.githubusercontent.com/brainforwarding/kanban.page/main/plugins/kanban/skills/kanban/SKILL.md): board/project selection and agent session handling - [README](https://raw.githubusercontent.com/brainforwarding/kanban.page/main/README.md): the app, sync, teams and the CLI - [CLI design](https://raw.githubusercontent.com/brainforwarding/kanban.page/main/docs/cli.md): the invariants the CLI holds to - [Full text for agents](https://kanban.page/llms-full.txt): this file, the README and the agent guide in one ## Optional - [Sync design](https://raw.githubusercontent.com/brainforwarding/kanban.page/main/docs/sync.md): the encrypted relay and the merge - [Team boards](https://raw.githubusercontent.com/brainforwarding/kanban.page/main/docs/team.md): shared boards, assignment and who finished what --- # Editing a kanban.page board This project's kanban board is reachable from the terminal with the `kanban` command. Use it instead of opening a browser. Check it is set up before relying on it: ```bash kanban board ls # lists configured boards; the default is marked * ``` If that prints `no boards configured`, stop and ask the person to run `kanban board add ` with their sync link. **Never ask them to paste the sync link into a chat, and never put it in a command argument** — it is the whole capability to their board, like a password, and it belongs in a pipe: `pbpaste | kanban board add work`. ## Reading ```bash kanban project ls --json # all projects, including empty ones kanban ls # the whole board, by stage kanban ls --stage Doing # one stage kanban ls --project Sales # one project kanban ls --all # include archived cards kanban show 4f2a # one card, with its notes kanban report # this week's moves kanban report --md # the same as markdown, ready to paste kanban report --week 2026-08-24 # an earlier week (must be a Monday) ``` Add `--json` to any command to get one object on stdout instead of prose. `ls --json` includes all projects, stages and roster members, plus the filtered `tasks` array. `--stage`, `--project`, `--mine` and `--all` apply to both formats; `--all` includes archived cards. Use `--project-id` and `--stage-id` when names are duplicated. Cards include project/stage IDs, assignee ID and archive status. ## Personal and shared boards `kanban board ls` lists locally configured connections. To discover shared boards linked from a configured personal board, use: ```bash kanban board discover --board work --json kanban ls --board work --team --json kanban project ls --board work --team --json ``` Discovery returns team IDs, names, member IDs and availability, never credentials. It changes no configuration. `available: false` means reconnect the team in the app. Discovery uses the selected connection's relay; custom relays still require an explicit `--relay `. Teams that have been left are excluded. Use an explicit board request or clear task context to choose the board. Inspect relevant boards before asking when uncertain. Keep `--board work --team ` on every command for a discovered team; its unambiguous name also works. A shared card should be updated on its own board, rather than duplicated on the personal board. Local-only unsynced boards cannot be reached through the CLI. Discovered teams use your identity synced from the app. `kanban whoami --board work --team ` reads it; identity changes happen in the app. Separately configured team connections still use the `whoami ` setup below. ## Writing ```bash kanban project add "New project" # only when a new project is requested kanban add "title" --project Sales --notes "..." --stage Inbox --flag kanban mv 4f2a Doing kanban done 4f2a kanban edit 4f2a --title "..." --notes "..." --project Sales --no-flag kanban archive 4f2a kanban restore 4f2a ``` On a **team board** (one with a roster): ```bash kanban whoami "Sebastián" # once per board: who this machine writes as kanban ls --mine # the cards assigned to me kanban assign 4f2a Andrea # hand a card to someone (or `nobody`) kanban session 4f2a "claude --resume …" # my private session for a team card ``` Moves on a team board are attributed to the `whoami` identity; without one they are unattributed and never count toward anyone's week. `--session` is refused on a team board — a session is private and lives on the personal board, which is what `kanban session` writes. For a discovered team, use `kanban session "codex resume …" --board work --team `; only the personal board is written. Codex personal-card sessions use a verified current `CODEX_THREAD_ID` and `codex resume`; Claude uses a verified `CLAUDE_CODE_SESSION_ID` and `claude --resume`. Include the correct absolute project folder, quote paths, and omit the session when its ID is unknown. Computers: `kanban here "Mac mini" --board ` once per machine, and every `--session` written from it records the computer and the folder. Card ids take any unambiguous prefix of 4+ characters. An ambiguous prefix fails and lists the matches — it never guesses. ## Rules that matter **The user's explicit request authorizes writes within that scope.** Ask before writing when no write was requested. A write reaches every device the person has open in about a second. Every mutating command prints the board it fetched first: ``` work · INBOX 27 / DOING 18 / DONE 63 · ver 412 ``` Use `--dry-run` to see that line and the intended change without writing. **Choose an existing project from context; ask when ambiguous.** Run `kanban project ls --json` on the selected board to see every project, including empty ones. Assign the card to the clear existing match. If no project fits, ask whether to create one and what to call it, unless already requested. **Project creation is explicit.** `kanban project add "Name" --json` returns its ID and name. Repeating an existing unambiguous name is a no-op. `--project Sales` on a card still requires an existing project; a typo is an error. Duplicate names are legal, so an ambiguous name fails with the IDs: pass `--project-id` or `--stage-id`. Stages are managed in the app. **`done` means the last stage, whatever it is called.** Don't look for a stage named "Done". **There is no delete.** `archive` is the strongest thing available. Permanent deletion, turning sync on, and ending sync all happen in the app, by a person. **Don't rerun a command whose outcome you don't know.** If one exits with `outcome-unknown`, the write may already have landed — run `kanban ls` and look before doing anything else. Re-running blindly can add the same card twice. ## Exit codes `0` success or no-op · `2` usage · `3` credential/config · `4` not found · `5` sync ended · `6` contention · `7` outcome unknown · `8` transport · `9` protocol. With `--json`, `error.code` carries the detail. --- # kanban.page kanban.page: a simple kanban for everything you're working on. Free, no account, works offline. A personal kanban that keeps your work organised by stage and by project. Free, works offline, no account. The board lives in your browser; turn on sync and it reaches your other devices through a server that cannot read it. **[Open the board →](https://kanban.page/)** ![the board](docs/shots/board.png) ## What's different **Your agent moves its own cards.** Once a board syncs it is reachable from a terminal, so Claude Code or Codex can read it, move a card and add one with no browser open anywhere — see [From a terminal](#from-a-terminal). And each card carries the session working it (`claude --resume …`, `codex resume …`), so you can see which thread owns which task: click it and it's on your clipboard, and copying one from a terminal then pressing `⌘V` on the board starts a card with that session already attached. **Friday's update is already written.** `R` opens the week — everything you created or moved, Monday to Sunday, split into what shipped and what is still in flight. Finished work arrives ticked; tick anything else worth mentioning. The export is exactly what is ticked, and narrower than the view — title only, no routes, no counts: ```markdown # Progress — 10–16 Aug 2026 ## Shipped - Onboarding tour v2 · Website - Rate limiting for the public API · API ## In flight - Invoice PDF export · API ``` Which stage counts as done is **position, not a name**: the rightmost one. So drag a new stage to where it belongs rather than renaming stages to fake it. **Your phone too, with no account.** **⋯ → Sync devices** shows a QR code and a link — scan it, and it's the same board on both. The link *is* the key, so anyone you give it to can read and edit the board, while the server only ever holds bytes it cannot decrypt. Every device keeps a complete local copy, and sync is reversible from either end. See [docs/sync.md](docs/sync.md). **A board for your team, beside yours.** The switcher next to the wordmark holds your personal board and your teams. **New team board…** gives you a link for your teammates; your name and pixel avatar come from your profile, set once from the face in the corner. On a team board a card can be assigned, the Mine pill counts what was handed to you, and a Done card says who moved it across the line. Your personal board stays yours; there are no private cards, because a shared key cannot keep secrets. Your teams follow you to every device paired to your personal board, and your weekly report covers both. See [docs/team.md](docs/team.md). **Every session knows its computer.** Register your computers once (⋯ → Computers) and a pasted `claude --resume` or `codex resume` remembers which machine it lives on — the card says *Mac mini* instead of leaving you to guess. On a team card your session is private: it lives on your personal board, and teammates never see it. See [docs/computers.md](docs/computers.md). **Want it to work differently? Ask your agent.** ~4,000 lines of vanilla HTML/CSS/JS, no build step, no dependencies, and a `CLAUDE.md` that lands an agent oriented instead of guessing. ![dark mode](docs/shots/dark.png) ## Fork it You get your own board at your own URL — and because it's a fork, you can pull new versions whenever you want them. 1. [Fork the repository](https://github.com/brainforwarding/kanban.page/fork). 2. **Settings → Pages → Source: GitHub Actions**. 3. The **Actions** tab → enable workflows, then push any commit. Yours is then at `https://.github.io/kanban.page/app/`, installable and self-updating, with your changes in it. ## Keys | | | |---|---| | `N` | new task | | `R` | weekly report | | `P` | projects | | `A` | archive | | `/` or `⌘K` | search | | `T` | light / dark | | `D` | compact cards | | `F` | flag / unflag the focused card | | `⌘V` | paste a resume command as a new card | | `⌥` + arrows | move the focused card between stages or up and down | | `Esc` | close whatever is open | Stage names are editable in place — click one and type. A stage can only be deleted once it's empty. ## Good to know - **Joining is explicit.** Paste a link through **Join with a sync link** on another computer. A device with an existing local board chooses Replace or Combine; a device already synced elsewhere must disconnect first. - **Move a board with `⋯ → Export backup`**, then `Import backup`. Worth doing occasionally regardless: a cleared browser is a cleared board. - **Nothing is one click from gone.** The board archives; only the archive deletes, behind a two-step confirm. - **Several boards:** open with `?ns=` and you get a separate one with its own storage. Handy as a scratch board. - **Install it:** visit once online, then use your browser's install menu. It opens offline after that, and offers **Update** when a new version ships — which changes the app files, never your cards. ## From a terminal Once a board syncs, it is reachable from a terminal — so a script, or an agent, can read and edit it without a browser open anywhere. Same relay, same merge rules, same end-to-end encryption; it is simply a second client. ```bash npm i -g kanban.page ``` Turn on sync in the app (**⋯ → Sync devices**), copy the sync link, then hand it to the CLI **through a pipe** — never as an argument, where it would land in your shell history: ```bash pbpaste | kanban board add work # macOS; stores the secret in the keychain kanban project ls --json # every project, including empty ones kanban ls # read the board kanban add "ship the thing" --project Sales kanban done 4f2a # ids take any unambiguous prefix kanban report --md # this week's progress, as markdown ``` Every write prints the board it fetched before it changes anything, because a write reaches every device you have open in about a second: ``` work · INBOX 27 / DOING 18 / DONE 63 · ver 412 + INBOX ship the thing · Sales ok · ver 413 ``` Add `--dry-run` to stop before the write, or `--json` to get one object on stdout instead of prose. JSON card lists honor the same filters as the text listing. `kanban help` lists everything. Projects can be created explicitly with `kanban project add "Name" --board work`. Card commands still reject unknown project names, so a typo never creates one. Shared boards linked from your personal board need no separate registration: ```bash kanban board discover --board work --json kanban ls --board work --team --json ``` Keep that board and team selector on subsequent commands. Discovery exposes no credentials and changes no local configuration. Separately registered shared boards continue to work with their own `--board` name. **The sync link is a password.** It is the whole capability — anyone holding it can read and edit the board, and there is no account to revoke. The CLI keeps it in your OS keychain, never accepts it as an argument, and never prints it. ### Letting an agent use it In Claude Code, install the plugin: ``` /plugin marketplace add brainforwarding/kanban.page /plugin install kanban@kanban-page ``` The plugin skill supports both Claude Code and Codex. For Codex, copy [`plugins/kanban/skills/kanban/SKILL.md`](plugins/kanban/skills/kanban/SKILL.md) into `~/.codex/skills/kanban/SKILL.md`. The CLI features described here require version 1.3.0 or later. When asked to track work, the agent finds an existing card before creating one, chooses the board and project from clear context, and asks when ambiguous. It creates a project only when requested. Session links use the current agent's verified session ID; shared-card session commands remain private on your personal board. Keep personal board preferences in `CLAUDE.md` or `AGENTS.md`. For any other agent, drop [`docs/agents.md`](docs/agents.md) into your own project as `AGENTS.md` (or paste it into a `CLAUDE.md`) and an agent has what it needs: the commands, the id rules, and the two things it must not assume. It only needs a board already registered with `kanban board add`. The CLI cannot create a board and never deletes one — sync is turned on, and ended, in the app. See [`docs/cli.md`](docs/cli.md) for the design and the invariants it holds to. ## Develop ```bash node --test tests/core.test.js # 114 unit tests, no dependencies node --test tests/team.test.js # 32 team-board and computer rules node --test tests/attachments.test.js # 15 image rules: merge, v4, crypto, headers node --test tests/relay.test.js # 11 relay image routes, against in-memory storage node --test tests/cli.test.js # 32 CLI tests, against a fake relay ``` They cover what is easy to get silently wrong: calendar dates across DST, Monday–Sunday boundaries across month and year ends, report aggregation, markdown output, storage migration, and the sync merge — clocks, tombstones, and three replicas converging. The CLI suite covers the ways a headless writer could lose work: a lost response double-adding a card, a retry restamping clocks, a concurrent delete losing to a stale edit, and a future schema being written back as an old one. Then open `tests/dom.test.html` in Chrome for 56 interaction tests — they drive the real app in an iframe and report pass/fail in the page title, against a `?ns=test` board that never touches your data — and `tests/team.dom.test.html` for 22 end-to-end team and computer flows (two people, one in-memory relay, scratch namespaces only). ``` index.html markup styles.css design tokens + every component core.js pure logic: dates, weeks, report aggregation, markdown, migration, merge app.js the app: rendering, drag, editor, projects, report, sync qr.js vendored QR encoder (MIT, Project Nayuki), loaded only by the sync sheet cli/ the headless client: same relay, same merge, no browser relay/ the sync relay: one Cloudflare Worker, deploy your own with `wrangler deploy` ``` ## License [MIT](LICENSE)