Skip to content
commission

Accounts, so the board knows who did what

You will be able to turn on sign-in for a Commission server several people share, create accounts for them, and have every action recorded against the person who took it rather than against the process.

Audience: whoever runs commission serve for a team. Assumes: a project with some work in it, as built in planning.md.

You probably do not need this

Commission defaults to auth.mode: open, which means the server acts as one identity — whoever started it — and asks nobody to sign in. On a laptop that is correct, and it stays the default forever. A single user should not have to run an account system to look at their own board.

Turn accounts on when the answer to “who did this?” stops being obvious: when more than one person can reach the server, or when it is not on localhost.

Create the people first

Turning on session mode before anybody has an account locks everyone out, including you. Accounts come first:

commission user add dana --name "Dana Ruiz" --password "correct horse battery staple"
commission user list

The first account is created by whoever is holding the terminal, and becomes an owner. It has to work that way: creating an account needs permission, permission comes from a role, and a role is set on an account — so if the first one were gated there would be no command that could ever make the second.

It is also the only account created this way, because it is the only one where the person typing the password is the person who will use it.

Passwords need twelve characters and nothing else. Length is the only rule that reliably helps, and the character-class rules everyone else imposes mostly produce Passw0rd!.

commission user add --password for somebody else means you choose their password and then have to send it to them — over Slack, in plain text, where it stays forever and is readable by everyone in the channel. Hashing it in the database does nothing about that copy.

So invite them instead:

COMMISSION_ACTOR=dana commission user invite priya --name "Priya Raman" --role maintainer

That creates the account and prints one URL:

http://localhost:4400/invite/cmi1_8WH72ZEMRX4u3_E9D7rYUiDGuWSIhUAet1JHUU8HLgY

send that to Priya Raman — it works once, and expires 2026-08-06
they choose their own password, so you never learn it

Priya opens it in a browser, types a password twice, and is signed in on the board. No terminal, and nobody else ever knows her password — which is the whole point. She needs no account to reach that page; it is the one page in Commission that a request with no credential may use.

The link is safe to send over the same channels a password was not:

  • single use — redeeming it spends it, so a link sitting in a mailbox after she has joined is inert. If a second person opens it, they get a page saying it has already been used and telling them to say so.
  • it expires, in seven days by default (--days 30 if a week is not enough for somebody on holiday).
  • re-inviting replaces it. Sent it to the wrong address? Run the command again: the new link works and the previous one stops.

Commission sends no mail. --email records an address on the account, and nothing is ever sent to it — delivery is whatever you already use to talk to the person, and Commission never learns whether the address is real.

The URL in the link is guessed from where commission serve listens by default. If the dashboard is somewhere else, say so — once, in the environment, rather than as a flag you have to remember:

COMMISSION_WEB_URL=https://commission.example.com COMMISSION_ACTOR=dana commission user invite jae --role observer

There is no self-registration. Commission is installed by a team rather than signed up for, so every account traces back to somebody who could already administer the install — and commission user list shows who has been invited and not yet arrived, as invited.

Somebody locked out

Re-invite them. It issues a fresh link, they choose a new password, and the old one stops working when they do:

COMMISSION_ACTOR=dana commission user invite priya

The blunter instrument still exists for when you cannot wait, and it has the cost this whole section exists to avoid — you will know their password:

COMMISSION_ACTOR=dana commission user passwd priya --password "a brand new passphrase here"

Neither one ends the sessions the old password opened. If the reason for the change is that it leaked, end them explicitly:

COMMISSION_ACTOR=dana commission user signout priya

Who may do what

observercontributormaintainerowner
read the board
note, ask, need
claim and move work
approve and verify
edit and cancel
manage accounts

observer is the role this exists for: a PM sees everything and can raise anything, without moving work around. It is not a lesser engineer — it is a different job, and commission need is a real lever rather than a consolation.

The other rows look generous, and that is deliberate: everything except account management already worked for everybody before roles existed, and an upgrade does not get to quietly withdraw permissions from a team that never asked for them. A team that wants tighter separation makes people observers, or waits for the enforcement to tighten in a release that says so.

COMMISSION_ACTOR=dana commission user role priya --role observer

Turn it on

{ "auth": { "mode": "session" } }

in .commission.json, or COMMISSION_AUTH_MODE=session for a single run. Then commission serve as before. Visiting any page now redirects to /login, and the API answers 401 with the two ways to get a credential.

The one exception is /invite/<token>, which has to work for somebody who has no credential yet — that is what an invite is. It grants nothing but the ability to set that one account’s first password, and only until the link is redeemed or expires.

Reads are gated too. Before this there was no read authorisation of any kind — every GET on both the API and the web view was open to anyone who could reach the port. If that was acceptable because the port was not reachable, it is worth checking whether that is still true.

Scripts and agents get tokens, not passwords

Something running in CI cannot sit at a login form. Issue it a token:

COMMISSION_ACTOR=dana commission user token priya --label "release pipeline"

The token is printed once, on stdout, so it can be piped straight into a secrets store. Only its hash is kept — a lost token is reissued, never recovered. Send it as Authorization: Bearer <token> and every action it takes is attributed to the person who owns it, exactly as if they had done it.

Give each script its own labelled token. They are cheap, and revoking one should never mean revoking all of them.

What is actually stored

Nothing you could use to sign in as somebody.

stored as
passwordargon2id hash
API tokensha256 hash
sessionsha256 hash of the token, plus its expiry
invite linksha256 hash of the secret, plus its expiry and whether it is spent

A sessions table containing the tokens it issued would be a table whose backup is a set of live logins, and whoever read it could act as anybody in the company. Storing the digest costs one comparison and removes that entirely.

Sessions are rows rather than self-contained signed tokens, which is what makes commission user signout take effect immediately instead of whenever the token would have expired.

What changes for anything already talking to the API

In open mode: nothing. Byte for byte.

In session mode, a POST /v1/... is recorded as the caller, where it used to be recorded as the server process. That is the entire point — but it means an integration that relied on everything being attributed to one identity will see its own name on its work instead. It is a change in what gets recorded, not in what is permitted — see the table above for that.

The whole sequence

commission user add dana --name "Dana Ruiz" --password "correct horse battery staple"
COMMISSION_ACTOR=dana commission user invite priya --name "Priya Raman" --role maintainer
COMMISSION_ACTOR=dana commission user role priya --role observer
COMMISSION_ACTOR=dana commission user token priya --label "release pipeline"
commission user list
COMMISSION_ACTOR=dana commission user signout priya

This page is docs/guides/accounts.md in the Commission repository, rendered in place — the site keeps no copy of it. The repository is private, so there is no edit link to follow.