renew
A parked login whose refresh token has expired is never due. Its account needs a sign-in again.
pitboard status without --offline renews too, by the access token rule alone.
renew asks Anthropic or OpenAI to renew each due parked login, with every request sent at once.
renew never renews a login a tool is using. A parked login that copies one is dropped instead, since renewing it would spend the refresh token the tool holds. That account needs no sign-in.
renew prints one line:
renew exits with status 0, even when nothing was renewed. Where Pitboard may change nothing, it fails with elevated or system_too_old.
schedule
The job runs
pitboard renew once a day, from the pitboard that installed it, by full path. If that file is gone, pitboard doctor fails its schedule check: see Daily renewal stopped working. Renewal schedule lists its files.
The job renews the parked logins in Pitboard’s own directory, ~/.pitboard or, on Windows, %USERPROFILE%\AppData\Local\Pitboard, whatever PITBOARD_HOME it is given. While PITBOARD_HOME names another directory, install refuses with schedule_not_default_home, and status says which directory the schedule renews. A link to the default directory counts as another directory. uninstall works whatever PITBOARD_HOME says.
The system starts the job without your shell’s environment. install gives it each proxy variable it was started with: ALL_PROXY, HTTPS_PROXY, HTTP_PROXY and NO_PROXY, in either case. Run install again after your proxy changes. Until then, pitboard doctor warns with schedule_proxy. The app’s Keep Saved Sign-Ins Fresh setting gives the job the app’s variables, not your shell’s: see The schedule uses another proxy.
- macOS
- Windows
- Linux
A LaunchAgent runs the job every 24 hours, the first time 24 hours after
install. launchd skips a run due while the Mac sleeps. install writes the proxy variables and PITBOARD_NO_ARGV into the job.install refuses with schedule_refused when the program’s path or a variable holds a character a LaunchAgent cannot hold, and names it. It refuses with schedule_program_temporary when the program is in the temporary copy macOS makes of an app opened where it was downloaded.\Pitboard\Renew S-1-5-21-….
vault
status reads Pitboard’s own files and, on Linux, asks your session’s bus whether a Secret Service answers, which can start your keyring there as any program’s first question to it does. It unlocks and changes nothing. Its first line says where they are:
Where KWallet answers for the Secret Service, as on KDE Plasma,
vault and move say KWallet in place of the login keyring.
On Linux, more lines follow, in this order:
Nothing moves parked logins between the two by itself.
move asks both forms what they hold first, and where either is locked or nothing answers for it, it refuses with credential_store_locked and changes nothing. Then it records the move in state.json, writes each parked login into the new form and reads it back, records the new form, and only then deletes each from the old one. A move cut short is finished by the next change or renewal, before anything else writes a parked login: pitboard status renews first, and so does daily renewal, which refuses as for any parked login where the store refuses the move. A parked login another Pitboard directory keeps in your login keyring stays there.
A parked login it could not delete from the old form, as from a login keyring locked meanwhile, is said with
parks_pending_removal and deleted by the next change. Another move deletes them first, and refuses while it cannot. On macOS and Windows, move refuses with vault_form_unsupported.
log
pitboard log prints the most recent changes, oldest first. Each line gives the local time, a verb, the subject and the outcome. The outcome is ok, a code from the table, or the error code that stopped the change.
With nothing logged, it prints Pitboard has not changed anything yet.
Only --json says which front end made each change, in caller: cli, app, or unknown for older lines.
The log is audit.log in Pitboard’s directory, and holds no email address. Past 256 KiB, Pitboard moves it to audit.log.1, replacing the one there, and pitboard log reads both.
uninstall
While the Pitboard app is open,
pitboard uninstall refuses with pitboard_open and deletes nothing: the app would make Pitboard’s directory again. Quit it with Quit Pitboard on macOS, or Exit in its icon’s menu on Windows. Otherwise it works in this order:
- On macOS, where Pitboard holds a Claude Desktop login or a move of one waits, reads Claude’s data folder as a change does, and settles the move. If it refuses, nothing is deleted.
- Stops and removes the watch service, then removes the daily renewal schedule. If either fails, it stops with nothing deleted.
- Deletes every parked login this Pitboard wrote, and every folder set aside in
~/.pitboard/parks. - Deletes Pitboard’s directory, once every parked login and folder set aside is gone.
app_open or uninstall_would_strand.
The login each tool is using stays. On macOS, so do parked logins that pitboard repair gave back and this Pitboard did not write. The keychain is shared, so another Pitboard may own them.
While a warm-up runs, step 4 keeps the directory. It also keeps it where a file in it could not be removed, and names that file. Run pitboard uninstall again once the warm-up is over or the file is closed.
With PITBOARD_HOME set to another directory, it deletes that directory and leaves the schedule and the watch service. pitboard schedule uninstall and pitboard watch uninstall remove them. The question and the output name that directory.
While CLAUDE_CODE_CUSTOM_OAUTH_URL is set, uninstall refuses with custom_oauth_endpoint. Custom OAuth endpoint says why.
In a terminal, it asks first, as Questions before deleting describes:
--json, it needs --yes. Without it, uninstall refuses with confirmation_needed, exit status 2, and changes nothing.
With two parked logins and daily renewal on:
On Windows, the question and the output name the directory by its path, such as
C:\Users\me\AppData\Local\Pitboard.
Remove Pitboard has the whole removal.
doctor
pitboard doctor prints one line per check: a mark, the check’s name and what it found. A line of advice follows where there is something to do. doctor changes nothing and sends no request.
The last line sums up:
doctor exits with status 3 when a check fails, and 0 otherwise. With --json, a failed check also sets ok to false and the error code to checks_failed. data still holds every check, as Data by command lists.
--json writes your home folder as ~, and replaces each of these with a digest of its kind, such as <email 1a2b3c4d>:
On Windows,
--json also writes your profile folder as ~ and your local app data folder as %LOCALAPPDATA%, and finds your user name and security identifier in any case.
Digests differ from one report to the next. Both reports write a proxy’s user name and password as ***. Otherwise the text report hides nothing.
doctor prints its own checks and Claude Code’s first. Each other tool’s checks follow under a heading, in this order:
When another tool’s checks appear and Claude Code is absent,
doctor leaves out config_file, identity, in_use, slot, credential_size, credential_store, credential, fallback_login, storage_v5, claude_daemon, claude_version, status_line, auth_source and login_restrictions. Claude Code is absent when it has no config file, no claude program and no stored login, and no Claude Code account is enrolled.
Some Codex levels depend on whether a Codex account is enrolled:
On macOS, Claude Desktop’s checks read the folders in
~/.pitboard/parks, open no cookie and list no process. Once a Claude Desktop account is enrolled, they also read Claude’s settings and the names at its data folder’s first level.
Every check
Every check
The checks in the order
doctor prints them.