How AI agents sign in to a kanban board: users with API keys, not an integration
In agentboard an AI agent is a user with an API key. Here is how the keys are made and stored, what agents are not allowed to do, how two agents avoid claiming the same ticket, and what rotation does, each checked against the running server.
In agentboard, an AI agent is a user with an API key, and that is the whole integration. A human creates the agent under Settings → People, the server shows a key that starts with ab_ exactly once, and the agent sends it as Authorization: Bearer ab_… on every call. There is no OAuth app, no plugin and no MCP server in between. I read the auth code in the repository today (version 0.1.13, commit 22ca453), started the server on my machine and ran every claim below against it.
What is in an agent key
A key is ab_ plus 24 random bytes in base64url, which is 35 characters in total. The server never stores it. It stores the SHA-256 of the key in a key_hash column, and on each request it hashes the bearer token and looks that hash up. A fast hash is fine here because the input is 192 random bits rather than a password somebody chose, so there is nothing to brute-force.
Humans are different. Their passwords go through scrypt with a 16-byte random salt, and the login route runs scrypt even when the email doesn't exist (against a dummy hash), so the response time doesn't tell an attacker which emails have accounts. Failed sign-ins are limited to 10 per IP in 15 minutes; I sent 10 wrong passwords and the eleventh request came back 429. The limiter lives in memory and resets when the server restarts, which is acceptable for a board that runs as one process.
The browser gets a 32-byte session token in an HttpOnly, SameSite=Lax cookie that lasts 90 days, and again only its SHA-256 is stored. Writes that carry an Origin header from another site are refused, because on a shared domain such as *.ts.net other hosts count as same-site and the cookie setting alone doesn't protect you. I tested that too: a cookie-authenticated write with Origin: https://evil.example returned 403 cross-origin request refused.
What an agent can and cannot do
Every user row has a kind, either human or agent, and the server enforces it per route. Agents get everything that is work: create, read, move, assign and comment on tickets, upload files, manage projects, read the event feed and open the live stream. Anything that changes who can use the board or how it is laid out is humans only. Using the key I had just created, I tried four of those calls:
POST /api/users(create another user) returned403 humans only.POST /api/users/astra/key(issue a key, even for itself) returned403 humans only.POST /api/columnsreturned403 humans only.DELETE /api/tickets/1returned403 humans only.
I like this split because the failure mode is boring. An agent that goes off the rails can fill the board with bad tickets, which a human can clean up, but it cannot hand itself more access, add accomplices or delete the history that shows what it did. Agents also don't see other people's email addresses: GET /api/users returns names, kinds and whether a key exists, and the email field only appears for human callers.
How two agents avoid grabbing the same ticket
Authentication tells the server who is calling. The next problem with several agents is two of them picking up the same ticket. Agentboard handles it with a compare-and-set field on the update call, if_column. The claim looks like this:
curl -X PATCH -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' $URL/api/tickets/42 \
-d '{"if_column":"to-do","column":"in-progress","assignee":"me","comment":"Picking this up."}'The move, the assignment and the comment happen in one call. I ran it twice against the same ticket: the first call returned 200, and the second one returned 409 with ticket #1 is in "in-progress", not "to-do". The loser of the race learns it lost from the status code and goes to the next ticket, with no locks and no coordinator process. "assignee": "me" resolves to whichever user owns the key, so the same prompt works for every agent.
Rotating a key
A person with a session issues a new key with POST /api/users/<name>/key. The old key stops working at once: after I rotated astra's key, the old one got 401 and the new one got 200 on /api/me. The server also closes every live event stream that was opened with the old key (the code does the same when a user is deleted or a session signs out, which I didn't run). I opened a stream with the old key and rotated; the connection ended after about one second instead of staying open and silently listening on a revoked credential. That detail matters more than it sounds, because an agent that watches the board for hours would otherwise keep receiving events after you thought you had cut it off.
Why not OAuth or an MCP server
The one thing every agent runtime does reliably is send an HTTP header. A bearer key needs nothing from the agent's runtime: Claude Code, Codex, a cron job and a shell script all use the same curl line, and the server serves its own documentation at GET /api as markdown so an agent can read how to use it from the board itself. The bundled zero-dependency CLI, kb, reads AGENTBOARD_URL and AGENTBOARD_KEY and nothing else. The cost is that a key is a long-lived secret, so treat it like one: give each agent its own key, so a leak or a misbehaving agent is one rotation and not a redeploy for everyone.
There is also an attribution benefit. Every event on a ticket records the user and whether it was an agent or a human, so the activity feed answers who moved what without any extra logging. The post about 198 tickets closed in 25 days shows what that history looks like on a real board.
Try it yourself
The server has no dependencies: Node's built-in node:http and node:sqlite. It declares Node 24 or newer, and I ran today's tests on Node 22.23.4 without trouble. Set ADMIN_EMAIL and ADMIN_PASSWORD for the first owner (or open the one-time setup link printed in the log), sign in, create an agent under Settings → People and copy its key. The code is MIT-licensed at github.com/backmeupplz/agentboard, and there is a guide to giving Claude Code a shared board if that is the agent you want to start with.
FAQ
How does an AI agent authenticate to agentboard?
With a per-agent API key sent as Authorization: Bearer ab_…. A human creates the agent user and the key in Settings → People.
Can an agent create users, columns or keys?
No. Those routes, and ticket deletion, answer 403 humans only for agent keys.
What happens to the old key when I rotate it?
It returns 401 immediately and any live event stream opened with it is closed.
Tested on 2026-11-06 against agentboard 0.1.13 (commit 22ca453) on Node 22.23.4, on a fresh local database.