agent-kanban is at 2.0.0 in the manifest and v1.13.4 in the tag list, and the license is a source-available one
An agent-first task board, Take human out of the loop.
At a glance
- What is it?
- A Cloudflare Worker and D1 board where agents claim tasks and humans review them, whose migration script refuses to run until a v2 upgrade check passes, whose local sign-in needs two external services, and whose license is recorded as NOASSERTION by the repository and FSL-1.1-ALv2 by the manifest.
- Who is it for?
- The design decision worth stealing here is the separation of duties inside the lifecycle: a task is assigned, claimed from an already verified session, worked, submitted, and then accepted or rejected by somebody other than the assignee, with a reject looping back to In Progress rather than closing anything. The dependency direction is also clean, with Enbor holding execution and Realmroot holding identity, and no Agent Kanban behaviour leaking into either.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 6, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The manifest reads 2.0.0 and the newest tag is v1.13.4
The release history ends at v1.13.4, published on 19 May 2026, after v1.13.3 on 18 May and v1.13.2 on 13 May. The manifest in the repository says version 2.0.0. So the 2.0 line exists in the tree and has never been tagged, and there is a second source of truth again: a `VERSION` file sits at the top level next to `package.json`. The 2.0 transition is not silent, though. Both migration scripts run a guard first, `db:migrate` executes `tsx scripts/check-v2-upgrade.ts --local` before applying D1 migrations locally, and `db:migrate:remote` runs the same check with `--remote` before applying them to the remote database. In other words, the project ships an explicit gate on its own major upgrade and wires it in front of every migration, which is the right shape for a schema change and the wrong shape for a version number nobody has tagged.
The license is NOASSERTION to the repository and FSL-1.1-ALv2 to the manifest
Two places record the terms and they do not match. The repository metadata carries no license assertion at all, and the manifest declares `license` as `FSL-1.1-ALv2`, with a `LICENSE` file at the root. Those are two different statements, and the project does not say which one governs. The manifest string itself is informative: the Functional Source License with an Apache 2.0 fallback is a source-available license, meaning the code is available to read and run and converts to Apache terms under its own conditions, which is a different arrangement from an OSI-approved license with no restriction. Combined with `private: true` in the same manifest, the practical effect is that the package is not published to any registry at all, so there is nothing to install from npm and the only route is a clone. Anyone deciding whether they may use this commercially should read the root `LICENSE` file rather than either of the two recorded values.
Local sign-in needs Realmroot and Enbor, not just a D1 file
The stack is one Cloudflare Worker with a D1 database behind it and a Hono API in front of the React application, and locally D1 works through Wrangler. The documented start is five commands:
git clone https://github.com/saltbo/agent-kanban.git
cd agent-kanban
pnpm install --frozen-lockfile
pnpm db:migrate
pnpm devThe moment you want sign-in, agents, machines, sessions or GitHub, two other services are involved. Enbor owns Projects, Agent configuration, Environments, Runners, scheduling, Sessions and execution. Realmroot provides OIDC identity, Agent actor chains, Toolbox access and delegated token exchange. The dependency direction is stated as always from Agent Kanban to those services, and the note that Enbor contains no Agent Kanban specific behaviour is there to keep the boundary clean. The cost is on the developer: the local server listens on port 6265, and the sign-in, agent, machine, session and GitHub flows need the configured Realmroot and Enbor services plus the matching secrets in `.dev.vars` before any of them work.
Execution provenance rests on a signing key you supply yourself
The provenance claim is specific: a Task Claim records the exact runtime and Enbor Session carried by the Agent's signed binding. The key behind that is developer-supplied. `.dev.vars` is where `OIDC_WEB_CLIENT_SECRET`, `OIDC_SERVICE_CLIENT_SECRET`, `AK_SESSION_ENCRYPTION_KEY` and `AK_SIGNING_KEY` go, and the two `AK_` values must each be canonical Base64 encoding exactly 32 bytes. GitHub App development adds `GITHUB_APP_WEBHOOK_SECRET` and `GITHUB_APP_PRIVATE_KEY`, and the instruction is never to commit `.dev.vars`. Public configuration and binding names live in `wrangler.toml`. So a claim is only as trustworthy as the signing key and the session encryption key on the machine that produced it, which is the right shape for a verifiable binding and a real operational responsibility for whoever runs the Worker.
The assignee cannot review its own work, and reject loops back
The lifecycle is the part with teeth. A Todo is assigned, which adds an agent without starting a runtime session. The agent claims it, moving to In Progress, and the claim comes from an already verified Enbor Session rather than creating one. Work happens, Notes are recorded, and a Review Submission moves the task to In Review, where it can be rejected back to In Progress or completed to Done. Cancel is available from the unstarted state. The rule underneath is that submitted work must be accepted or rejected by an authorized actor other than the assignee, and the assignee cannot accept or reject its own submission. Dependencies are also enforced at the model level, with tasks able to depend on other tasks while cycles and cross-tenant relationships are rejected. This is the piece that makes the board agent-first rather than a chat window with columns.
Public views are read-only and observation grants no control
Two escape hatches are deliberately narrow. A board can be published as a read-only view that follows updates without exposing the authenticated workspace, so sharing progress with someone outside the tenant does not mean sharing the workspace. And authorized humans can inspect the exact bound Enbor Session for a task without gaining runtime control, which separates watching from steering. Both are the kind of boundary that is easy to state and easy to erode, so they are worth testing rather than trusting: a read-only view that leaks a task identifier is still a leak, and an observation endpoint that accepts a control verb is a control endpoint. The reference pages for the boundary are a system overview and a task lifecycle document, both under `docs/architecture/`.
Skills are generated from the spec, and lint fails when coverage slips
Agent-facing material is built, not hand-maintained. `build` runs `build:skills`, which executes `node scripts/build-agent-skills.mjs`, and `check:skills` runs the same script with `--check`, so a stale generated file is detectable. Lint is `biome check .` chained with `lint:spec`, which runs `tsx scripts/check-spec-coverage.ts`, so a spec gap fails the lint step rather than waiting for review. A `spec/` directory holds the source, and the published OpenAPI document is named as the truth for resources, schemas, scopes, pagination and generated commands. On the client side, Realmroot Toolbox operations are generic and verb-first, `task wait` is the only generated resource-first convenience command, and Toolbox v0.5.0 or newer generates required idempotency keys and reuses them across transient retries, with an explicit key needed only when recovering an invocation whose outcome stayed unknown.
Four test configs, generated hooks, and a skills table that ends mid-word
The top level carries `playwright.config.ts`, a second `playwright.isolated.config.ts`, `vite.e2e.config.ts` and `vitest.config.ts`, so end to end runs and unit runs are configured four times over, and `prepare` runs `lefthook install`, which installs git hooks on every install. Formatting and linting are both Biome, configured in `biome.json`, and `components.json` is present for component scaffolding. Two agent instruction files sit at the root as well, `AGENTS.md` and `CLAUDE.md`, next to `DESIGN.md` and a `.claude/` directory. The skills table has three rows and the third ends before its sentence does: `ak-task` is for creating, assigning, monitoring and reviewing one Task, and its invocation rule stops at the phrase explicit user invocation requir. The other two are complete, with `ak-worker` allowing automatic invocation and `ak-maintainer` described as planning, coordinating and reviewing multi-task work inside an authorized project.
Editorial conclusion
The design decision worth stealing here is the separation of duties inside the lifecycle: a task is assigned, claimed from an already verified session, worked, submitted, and then accepted or rejected by somebody other than the assignee, with a reject looping back to In Progress rather than closing anything. The dependency direction is also clean, with Enbor holding execution and Realmroot holding identity, and no Agent Kanban behaviour leaking into either. Before you build on it, settle four things. The version you get is 2.0.0 in the manifest with no matching tag, and a `VERSION` file beside it, so nothing says which is authoritative. The license is FSL-1.1-ALv2 in the manifest while the repository metadata asserts nothing, and that is a source-available license rather than an open source one. Local sign-in needs Realmroot and Enbor reachable, not just a D1 file. And the review gate, the provenance signature and the public views are all enforced in one Worker, so the security review has to cover the review path, the signing key in `.dev.vars`, and the read-only board surface together.
Frequently asked questions
How does agent-kanban stop an agent from reviewing its own work?
Submitted work must be accepted or rejected by an authorized actor other than the assignee, and the assignee cannot accept or reject its own submission. A rejection returns the task to In Progress rather than closing it, and the claim that starts the work comes from an already verified Enbor Session.
What does it take to run agent-kanban locally?
Node.js 24 or newer, pnpm 10 and Wrangler 4, then install with a frozen lockfile, run the migration and start the dev server on port 6265. D1 works locally through Wrangler, but sign-in, agent, machine, session and GitHub flows also need the configured Realmroot and Enbor services plus secrets in .dev.vars.
Which secrets does agent-kanban need and how are they generated?
OIDC_WEB_CLIENT_SECRET, OIDC_SERVICE_CLIENT_SECRET, AK_SESSION_ENCRYPTION_KEY and AK_SIGNING_KEY, where the two AK values must each be canonical Base64 encoding exactly 32 bytes. GitHub App development adds GITHUB_APP_WEBHOOK_SECRET and GITHUB_APP_PRIVATE_KEY, and .dev.vars should never be committed.
How are the agent skills in agent-kanban produced?
They are generated. The build runs node scripts/build-agent-skills.mjs, check:skills runs it with --check, and lint chains a spec coverage check so a gap fails lint. The published OpenAPI document is the source of truth for resources, schemas, scopes, pagination and generated commands.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/saltbo-agent-kanban)