Releases: datlechin/pitboard
Release list
v0.5.1
Fixes that writing the documentation site turned up, and pitboard's facts about Claude Code
read again, from 2.1.284.
Added
- In
pitboard-core,assumptions::read_onsays which systems' builds a fact is read from,
as the newassumptions::Platform.
Changed
- pitboard's facts about Claude Code are read from 2.1.284, and from its macOS build as well
as its Linux one. The weekly check reported three facts moved from 2.1.281; all three were
read from the Linux build, which has no keychain code, or from the runtime Claude Code
ships in. The one real change is that Claude Code no longer moves its login to the
plaintext file when the keychain is locked, and nothing in pitboard depends on the old
behaviour. - The README introduces pitboard and links to
docs.usepitboard.com, where the guides and reference are. SECURITY.mdis only the security policy: which versions get fixes, how to report a
vulnerability, and what pitboard does and does not protect against. Where parked logins
are kept and what leaves your machine are on
Security and privacy.- How to make a release, how to replace the update key, and the Sparkle and Homebrew
measurements a release rests on, moved fromCONTRIBUTING.mdtoRELEASING.md. - How the code is laid out, and the measurements of macOS, Claude Code and Codex it rests
on, moved toARCHITECTURE.md.
Fixed
pitboard status --fresh, and Refresh in the app, asked Anthropic or OpenAI again during
a wait the service had asked for. The 0.3.0 notes andpitboard doctorsaid they did not.
They keep that wait now, and still ask a service again after it could not be reached.
When a service had never answered for an account and then could not be reached, pitboard
said the account was rate limited.- With
PITBOARD_NO_ARGV=1, renewing a parked login too large forsecurity's standard
input spent its refresh token and then could not store the new one. The parked login was
lost. pitboard refuses before asking the service now, and the parked login stays as it was. - A switch whose login could not be read back said to run
pitboardagain, which only
reads. It names thepitboard usethat finishes or undoes the switch. - With Codex's
auth.jsonmissing,pitboard doctorsuggestedpitboard use codex/<label>,
which refuses while nothing is signed in. It says to sign in withcodex login. - The man page listed a page per command, such as
pitboard-status(1), and none of them is
installed. It sets out every command, with its arguments and options, on its one page. - The app never said that Claude Code is not installed: it waited for a read to fail in a
way no read does. It says so when it finds neitherclaudenorcodex, and neither tool
has a login or an enrolled account.
Security
- The menu bar app ignored
PITBOARD_NO_ARGV, and so did daily renewal unless its
scheduler set it. Either could pass a large login tosecurityas an argument, which
another process running as you could read while the call lasts. The app reads
PITBOARD_NO_ARGVfrom its own environment, and a schedule installed while it is set
keeps it.
v0.5.0
Changed
- The menu bar item opens a menu instead of a panel, as macOS asks of a menu bar item. Each
account is an item under its tool, checked when it is the one in use and subtitled with
what its limits stand at, and choosing another switches to it. Advice to switch, anything
else worth a look, and an update that is ready come first. It opens at once, closes the
way every menu does, and works from the keyboard and with VoiceOver. - Everything that needs typing or room is in pitboard's window: every account with its
limits drawn out, everything pitboard has to say in full, the activity log, and what it
finds about this Mac. Adding an account, signing in again, naming the account in use and
renaming one are sheets over it, and a sheet stays open while your browser is in front. The
panel closed the moment the browser came forward, and took the sign-in's code field with
it. - A switch, a rename or a forget that fails says why in an alert, instead of in a line of
the panel that the next read could replace before anyone saw it. A sign-in or a name that
fails says so in its sheet, with what you typed still there. - Settings has a Command Line tab of its own. "Open pitboard at login" says when macOS is
waiting for you to allow pitboard in Login Items, and opens them. "Menu bar shows" picks
the account and its usage, the usage alone, or the icon alone, for a crowded menu bar.
Added
- Rename an account from the window, which only the command line could do.
- In the window's account list: Use with a double-click or Return, Forget with Delete, and
Copy Email Address and Sign In Again in each account's menu. - About pitboard in the menu.
Fixed
- The panel said "updated just now" for as long as nothing else changed. The menu says the
time of the last read, and the window's reset times move on while it stays open. - Giving up on an interrupted switch was reported with a warning sign, as though it had
failed. It is a note now, which you dismiss once read. - A failure to turn daily renewal on or off was said in the panel rather than beside the
switch in Settings, and a failure to change "Open at login" was not said at all. - Switching one tool's account put away the advice about another tool's account that had
run out, and it was not offered again until that account ran out once more. - Advice to switch kept offering the account it first named after that account was
forgotten, expired or ran out itself, and choosing it failed. It now offers the best
account there is at each read, and goes away when there is none. - A read that was already waiting on Anthropic or OpenAI when you switched landed after the
switch, with who was signed in before it, and put away what the switch said, such as the
reminder to restart runningcodexsessions. A switch made in a terminal during such a
read was taken as seen, and the menu bar went on naming the account before it until the
next read. - Why an account's numbers are not new was said only for an account that could not be
used. The window says it for every account, the one in use included: that its service
could not be reached or is rate limiting, or that Claude Code's session has expired. - The panel and the window described accounts differently: the window had no way to sign in
again or forget an account and did not say how long a parked login lasts, and the panel
did not say how long the account in use lasts at its rate. Both now say the same things,
worked out once.
v0.5.0-rc1
pitboard 0.5.0-rc1
v0.4.1
Changed
- A usage reading only moves forward. pitboard keeps one reading per account, every front
end records into it and every front end shows it. A later reset is a newer window, and
within one window the higher share is the newer, so numbers a session has held since its
last response can no longer replace newer ones, whoever writes last. Where a window's
share falls, as it does when a plan is upgraded in the middle of one, the old, higher
share stands until that window resets. - The app's Documentation item opens docs.usepitboard.com.
Fixed
- Sessions on one account, and the menu bar, disagreed about the account in use. Each
session showed the numbers of its own last response, so busy sessions read 22%·6% while an
idle one read 20%·5%, and the menu bar showed what it had last asked Anthropic, or Claude
Code's own cache. The status line now records what its session's responses bring wherever
it is newer, every session shows the newest numbers any of them has recorded,pitboard statusdoes the same, and the menu bar follows the readings within seconds without
asking anyone. The README's status line settings add"refreshInterval": 10, so an idle
session picks them up too. - After a switch, a session still holding the numbers of the account before could record
them as the account switched to, until the next read. A session's numbers do not say
whose they are, so the status line now keeps what each session passed at its last run,
and which account Claude Code's config named then, in~/.pitboard/sessions.json. It
records only what a session's response moved with the same account named before and
after, and nothing in the half minute sessions take to follow a switch, so a session left
idle is never recorded as anyone, whatever has happened to other accounts since: a
switch, a/login, an account forgotten. What a session passes the first time pitboard
sees it is left out, and so is what it passes as the account named changes; its next
response is recorded. A/loginin Claude Code leaves pitboard no time to count from,
and for the half minute after one a session's response can still be the account
before's. The status line leaves that out where pitboard's reading of the account before
has the same window; otherwise, or where the two accounts' windows reset within the same
minute, the account signed in after can show the higher of their shares until that
window resets. - The panel's advice to switch, once the account in use had run out, went away at the
app's next read, as soon as the panel was opened again, while the account was still out.
It now comes as soon as the numbers show the account run out, stays for as long as they
do, and is told once.
v0.4.1-rc1
pitboard 0.4.1-rc1
v0.4.0
Changed
- Installing pitboard no longer needs Rust.
brew install datlechin/tap/pitboardis now a
cask, on macOS and Linux, that installs the release's own command line for the machine,
with its man page and completions: signed and notarised on macOS, attested, and checked
against the checksums the release took of its own files. It was a formula that fetched
Rust and compiled pitboard, which took minutes and a toolchain nobody had asked for. - The menu bar app's cask is
pitboard-app, and the app carries the command line inside
it, atPitboard.app/Contents/Helpers/pitboard. The cask links it ontoPATHwith its
man page and completions, so an update, from Sparkle or from Homebrew, moves the app and
the command line together. They used to be two installs that moved separately, and the
app's cask depended on the formula. The two casks conflict, since both linkpitboard.
The app's bill of materials lists the command line and the crates only it uses. The
release writes the tap's own README with the casks, so it names both. - A release no longer publishes a source tarball. Only the formula installed from it, and
the source is on crates.io and in the tag. pitboard-core: the reportuninstallreturns says whether the renewal schedule was
taken away, and it anddoctor::Factsare now#[non_exhaustive], so a later field is
not a breaking change. A breaking change for anyone who built either with a literal,
declared as such; nothing changes for the command line or the app.
Added
- Settings can put the app's command line on the
PATH. The Advanced tab says which
pitboarda terminal runs, whether it is the app's own and how that one is updated, and
when there is none, "Install command line tool…" links/usr/local/bin/pitboardto the
one inside the app, once macOS has asked for an administrator's password. It never
replaces apitboardsomebody installed or a file that is not a link, and never links to
the temporary copy macOS runs an app from before it is moved to Applications. - An account whose parked login has expired has a "Sign in again" button in its row, which
starts the same sign-in as adding an account. The row used to say to runpitboard enroll <label> --sign-inin a terminal, which somebody with only the app does not use.
Fixed
- Daily renewal turned on from the app renewed nothing. The schedule recorded the program
that asked for it, which from the app was the app itself, so launchd started a second
menu bar app every day and no parked login was renewed. The app now names the command
line inside it. A schedule an older app wrote starts the app, which now hands the renewal
to that command line, so the old schedule keeps renewing until the app is opened. Opening
it then points the schedule at the command line, whichpitboard logrecords. The app
turns renewal on only where that command line will still be there when the schedule runs:
not from the temporary copy macOS runs an app from before it is moved to Applications,
which is gone once the app quits, and not from a build with no command line inside it.
Settings says why. New codes, from the app's bindings:schedule_program_missing,
schedule_program_temporaryandschedule_program_unnamed.pitboard doctorreads the
installed schedule back and fails when thepitboardit runs is gone or is an app, and
says to turn renewal off and on again. - On Linux, a renewal schedule turned on from the command line stopped working at the next
brew upgrade. It named the running pitboard with every link resolved, which from
Homebrew is inside a directory named after the version, and the upgrade deletes that
directory, so systemd failed to start it every day after. It now names the path pitboard
was started by, such as the link in Homebrew'sbin, when that leads to the same program. - Removing pitboard leaves no renewal schedule behind.
pitboard uninstalltakes it away
first and says so, asschedule_removedin--json, where before it was left running
pitboard renewevery day. Both casks take it away onbrew uninstall --zap, and not on
a plainbrew uninstall, because Homebrew runs a cask's uninstall steps on every upgrade
too. Neither touches~/.pitboard: it is the only index of the parked logins, so run
pitboard uninstallbefore removing pitboard. - Advice about upgrading and removing pitboard no longer assumes Homebrew. A state file
from a newer pitboard said to runbrew upgrade pitboard, andpitboard uninstallsaid
to remove the binary with a package manager. Both now say to update or remove pitboard
the way it was installed, and the first adds that the app's Check for Updates moves only
the app and the command line inside it. - On Homebrew 6 and later,
brew install --cask datlechin/tap/pitboardfailed with
build.rb ... exited with 1unless the formula was installed first. Homebrew trusts only
the name it is asked to install, and refused to build the formula the app's cask depended
on.pitboard-appdepends on nothing. cargo binstall pitboardno longer falls back to a third party's build when it cannot
fetch the release's.
Upgrading from 0.3.0
In the tap, the name pitboard now means the command line.
From the old formula, brew update warns that it did not install the cask that replaces
it, and pitboard stays at 0.3.0. The two commands it prints leave the formula in front of
the cask, so install the cask in its place instead:
brew uninstall --formula pitboard
brew install datlechin/tap/pitboardFrom the old app cask, brew update replaces the app with the command line, once. Your
settings and ~/.pitboard stay. To get the app back, with the command line inside it, run
these in this order, before or after that brew update:
brew uninstall --cask pitboard
brew uninstall --formula --force pitboard
brew install --cask datlechin/tap/pitboard-appLeave --zap out when you remove the old app cask. Its zap moves ~/.pitboard to the
Trash, and Homebrew runs the zap of a cask as it was installed, whatever the tap says by
then.
A copy of the app from a release updates itself as before and brings the command line with
it.
If you turned on daily renewal in a 0.3.0 app, its schedule ran the app itself and renewed
nothing. Once the app from this release is in its place, the old schedule keeps renewing,
through the command line inside the app, until you open the app. Opening it then points the
schedule at that command line. A schedule that runs a pitboard that is not there any more
is left as it is. On Linux that includes one turned on with the formula: it ran the copy
inside the formula's own directory, which goes with the formula. Turn such a schedule off
and on again, in the app's Settings or with pitboard schedule uninstall and then
pitboard schedule install. pitboard doctor from this release says whether yours needs
it, and so does Settings, Advanced, "Check this machine".
v0.4.0-rc1
pitboard 0.4.0-rc1
v0.3.0
Added
- Codex, beside Claude Code.
pitboard enroll codex/workrecords the Codex account signed
in now, reading its account, email and plan out of its own ID token with no network
call;pitboard enroll codex/work --sign-inrunscodex loginin a private
CODEX_HOMEso the account in use stays signed in;pitboard use codex/workswitches
it; andpitboardshows each Codex account's five-hour and weekly limits from the same
usage read Codex itself makes, which spends no quota. Until a Codex account is enrolled,
pitboard reads nothing of Codex's and asks OpenAI nothing. Everything pitboard does for Codex
was read out of codex-cli 0.154.0 and is dated in a register of its own, checked against
every new build by the conformance run twice a week, and still green on 0.156.1.
Codex is not Claude Code, and pitboard says where they differ rather than hiding it:- A running
codexnever notices a switch, so a switch says to restart it instead of
counting down, and names how many sessions are still using the outgoing account. codex loginandcodex logoutrevoke the stored refresh token, so a Codex park is
never a copy: the outgoing login is moved into the vault and read back before
anything replaces it. Signing out inside a session still running from before a
switch would revoke the login just parked, and the switch says so.- Codex's keychain stores are items Codex made for itself, which every read by another
program would put a permission prompt in front of, so pitboard handles Codex's
default store, theauth.jsonfile, and refuses the others with a reason. - Codex takes no lock, so a session still running from before a switch can refresh its
token in the middle of one. The live login is read again just before it is replaced,
and nothing is written over a login that moved; a login that ends up naming two
accounts is refused rather than parked.
- A running
- The menu bar app handles both tools. With accounts of both, the panel lists them under a
heading per tool, and the menu bar follows the signed-in account closest to running out.
When an account runs out, only another account of the same tool is offered. A Codex
switch has no countdown: the panel says runningcodexsessions keep the old account,
and how many pitboard found, and keeps saying so until that tool switches again rather
than until anything at all changes. "Add another account" asks which tool when more than
one is installed, and a Codex sign-in shows the addresscodex loginprinted. Cancelling
a sign-in no longer holds the app until the tool says something, which a Codex sign-in
never does before the browser is done. - Labels belong to a tool.
workcan be a Claude Code account and a Codex account at
once,codex/worksays which, and a bareworkstill means what it always did as long
as it names one account; where it names two, pitboard lists both rather than picking.
A bare name for a new account means Claude Code, so every command written before there
was a second tool does what it did. New codes:provider_unknown,label_ambiguous,
sign_in_not_isolated,live_store_unsupported,state_names_unknown_tool,
recovery_elsewhere,sessions_still_running,codex_program_missingand
codex_not_found. - No parked login goes unnamed. Every name pitboard is about to write a login into is
written down first, and the next command resolves any that nothing refers to: given back
to the account whose name it carries when that account holds nothing, deleted when nobody
wants it, and left alone when the store could not be read. Before this, a run killed
between writing a login and recording it left a live refresh token that no entry named,
never renewed, never deleted bypitboard uninstall, and on macOS not listable by any
tool a person has.pitboard doctorreports anything still outstanding. pitboard adopttakes over a~/.pitboardthat another computer wrote. The machine
stamp is right and its reason is sound, but the refusal stopped every command including
uninstall, so somebody whose home and keychain arrived through Migration Assistant met
a tool that would not do anything and an error telling them to enrol again with no
command that made it possible. Adopting keeps what is a fact about an account, the label,
the email, the uuids and the remembered numbers, and drops every parked login, because a
login belongs to the computer that signed in. It deliberately does not ask Anthropic
whether a parked token still works: finding out means exchanging it, and exchanging it is
the act that would rotate it past the other machine's copy.pitboard repairasks the credential store itself what parked logins are on this
machine, rather than reading pitboard's own index, and accounts for every one it finds:
given back to the account whose name it carries, or deleted when no account here wants
it, or reported and left exactly where it is. Only a name this pitboard wrote down itself
is ever deleted: a keychain belongs to a whole login session while pitboard's records
belong to onePITBOARD_HOME, so a parked login it cannot account for is evidence of
another pitboard rather than of an orphan, and deleting it would end that account's
session for somebody who never ran the command. Giving one back is additive and safe on a
guess; deleting one is not, so on macOS a login given back that this pitboard never wrote
down is deleted only once pitboard has used it, by switching to it or renewing it, even
when the renewal is stopped before it records what it got back. Parking over it,forget
anduninstallleave it where it is, anduninstallsays how many it left, as
parks_leftin--json. Elsewhere the vault is a directory inside pitboard's own, which
no other pitboard parks in, so whateverrepairfinds there is this one's. Measured
first:security dump-keychainwithout-dnever prompts, takes 0.06 seconds, emits no
secret of any item, and does not slow later reads.- A crash matrix: every durable step of a switch, an enrolment, a renewal and a forget,
killed where it stands, recovered, and checked against what must be true afterwards
rather than against a particular outcome. Every case runs recovery twice, because a
recovery that only works once leaves a machine nobody can fix, and once with Anthropic
unreachable, because the answer then must be to change nothing. The two windows above
are what it found on its first run. - A failure that came from asking Anthropic now carries a cause beside its code, in both
--jsonand the app's bindings:unreachable,rate_limited,server_error,
answer_not_understood,login_refusedortoken_expired, each saying whether asking
again is worth anything. Before this, everything that was not a 401 arrived as
identity_unverifiableand a sentence of prose, so nothing could tell being offline from
being rate limited from a login Anthropic had finished with.statustells the same three
apart too, where they used to share one code. - The app follows a switch made anywhere else on the machine. Three front ends ran on one
machine and none could tell when another had changed something, so a switch typed in a
terminal left the menu bar naming the account the person had just stopped using for as
long as five minutes, with a button offering a switch that had already happened. The app
now asks every couple of seconds when pitboard's account index last changed, which is one
stat of one file, and re-reads what it already knows when it moves: no network, no
keychain and nothing asked of Anthropic. Deliberately the index alone and not the whole
directory, because the status line writes usage readings after every message in every
open session. - The app has a window and a Settings scene. The panel is 400 points wide and everything
that needed more than that either expanded inside it or sent the person to a terminal,
and everything configurable lived in an ellipsis menu where opening at login sat between
hiding the checks and quitting. The window holds each account with what its limits have
been doing, the whole log of what pitboard has changed, and all of what it found about
this machine. Settings opens with Command-comma where people look for it, and reaches the
scheduled renewal the core could already do and the app could not. - A first run for somebody who installed only the app. The cask puts the command line on
the machine too, but an empty panel used to say "Runpitboard enroll <label>" and a
machine without Claude Code said one line of error, which between them sent every new
person to a terminal to find out whether the thing they had just installed worked. The
app now names the state it is in, no Claude Code, nobody signed in, signed in but
unnamed, or one account with nothing to switch to, and offers the one next step, each of
which it can do itself. Nothing is asked before the first read. The first launch ever
opens the window, because a status item is invisible to somebody who has just installed
it. doctorreads the modes of everything on the disk that holds a login: the plaintext
credential file, pitboard's vault and every parked login in it, and fails when anyone
but the owner can read one. Where there is no keychain, a mode bit is the whole of that
protection, and a backup restore, acp -ror a careless umask changes one quietly.- The app's bindings reach the rest of the core: the offline report, giving up on an
interrupted switch, the change log, renewing, and the renewal schedule. A read that cannot
reach Anthropic now falls back to the last numbers measured rather than an empty panel,
which said the accounts were gone. An interrupted switch that recovery cannot finish has
a way out in the panel rather than sending somebody to a terminal, which is the...
v0.3.0-rc1
pitboard 0.3.0-rc1
v0.2.0
Everything a stranger hits in the first ten minutes, every state a person could be stuck in,
and what the app was missing to stand on its own.
Added
pitboard logshows what pitboard has changed and when, from the record it was already
keeping. The log now names which front end asked.pitboard uninstalldeletes every parked login and then pitboard's own files, in that
order, because the account list is the only index of those keychain items.pitboard abandongives up on an interrupted switch that cannot be finished, keeping
every login. The way out when recovery cannot reach Anthropic.pitboard status --offlineanswers from what was last measured, without asking Anthropic
or touching a login. Milliseconds instead of seconds, and it works with no network.forgetasks before deleting a parked login, unless--yesor--json.- Each usage row in
--jsoncarriesseverity, Anthropic's own grade for that limit. A
field added to the v1 envelope; nothing was removed or renamed. - The app: each account's email, when each limit resets, when a parked login stops working,
doctor's checks on demand, its own version, a mark in the menu bar, and VoiceOver labels.
It can record the account in use, drop an account, and run Claude Code's own sign-in for
a new one, showing what that sign-in says rather than borrowing a terminal. cargo binstall pitboardfetches the built binary instead of compiling the tree.
Fixed
pitboard statuslinetyped at a prompt waited for input that was never coming. It reads
stdin only when something is piping into it.- Offline, the account in use rendered as one with nothing parked, advising a sign-in it did
not need. - A switch that failed before installing anything left its journal behind, so the next
command announced a recovery for something that never happened. enroll --sign-inchecked what could refuse the enrolment only after the browser sign-in.- The status line showed
?·?for every account but the one in use until someone ran
pitboardby hand. It now keeps the numbers Claude Code hands it. - doctor and forget read pitboard's record of its last switch rather than who is signed in,
so a sign-in made with Claude Code's own/loginmade both wrong. - Three ways a parked login could be left in the keychain with nothing naming it.
- A renewal that could not be written left the account with a login already spent.
- The keychain ceiling has its own error, saying the size, the limit, and what to do. A
login with MCP server tokens in it is past that limit, which is not theory. ANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKENandCLAUDE_CODE_OAUTH_TOKENraise a warning
on every change: Claude Code signs in with those, not with the login pitboard moved.- Columns line up in what a terminal draws, so a label in Chinese or Japanese no longer
pushes everything after it out of line. - One state file serves every credential slot, and what was switched to in one slot is no
longer claimed in another. - The menu bar showed the largest percentage of any limit, so a row scoped to one model
read as though everything had stopped. - Windows gets one sentence instead of a screen of type errors.
Internal
- MSRV is 1.91, measured by building it, and CI builds at whatever the manifest claims.
- The state file can be read forwards, and says which half to upgrade when it cannot.
- A release is guarded, re-runnable, and carries build provenance; the macOS command line
binaries are signed and notarised like the app. A tag likev0.2.0-rc1is a pre-release:
no crates.io, no update feed. - The app can be tested without a keychain, and is.