Repository navigation
v0.8.0
Added
- Pitboard can switch Claude Code by itself before the account in use runs out, so an agent
working in a runningclaudesession can go on under another of your accounts. Once any
limit of the account in use reaches a share you choose, 95% by default, Pitboard
switches to the enrolled account with the most room in that limit, among those that have
used less than the share of every limit. With no such account, nothing moves. It
switches before the limit, not at it, because sessions already running follow a switch
within about 33 seconds. It is off until you turn it on. It never switches Codex, whose
running sessions keep their account until restarted, and never switches back by itself.
Each switch is the onepitboard usemakes, andpitboard logrecords it as
auto-switch. What was tried for each limit is kept inautoswitch.jsonin Pitboard's
directory. See Switch before an account runs
out. pitboard watchswitches Claude Code that way for as long as it runs in a terminal,
whatever the app's setting says. Control-C stops it. Nothing installs it, and it starts
nothing that outlives it.--atsets the share, from 50 to 99. It reads every account
every 5 minutes, as the app does, and decides again within 2 seconds of a status line
recording usage. It prints a line, starting with the local time, for each switch, and
once for each thing that stops one.--oncedecides once from the numbers Pitboard last
read and exits. With--json, each event is an envelope on a line of its own, whose
data.eventiswatching,switched,no_room,skippedoridle.- The app's Settings > General has a Before an account runs out section, with
Switch Claude Code automatically, off by default, and the share it switches at, from
50% to 99%. The app keeps both inapp.json, asauto_switchandauto_switch_at, and
switches only while it is open. After a switch it posts a notification such as
Switched Claude Code to personal, and the window shows the switch as one made by
hand. When something stops a switch, or a switch it tries fails, it says why in a
notification, once until it next switches. The Activity pane names such a switch
Automatic switch. pitboard doctorhas anetworkcheck, which the app's This Mac pane shows too. It
says whether Pitboard's requests go out directly or through a proxy, names the proxy and
the variable it came from, such as
through http://***:***@proxy.example.com:3128, from HTTPS_PROXY, and lists the hosts
Pitboard calls thatNO_PROXYexempts. Neither report shows the user name or password
in a proxy's address, and--jsonshows a digest in place of its host and port, as it
does an organisation's name, unless the proxy is on loopback. It warns when the proxy is
a SOCKS one, which Pitboard does not go through, in the words a request then fails with,
unlessNO_PROXYexempts every host Pitboard calls. It also warns when a variable holds
no proxy address Pitboard can read, which Pitboard passes over.- The daily renewal schedule goes through the proxy of the Pitboard that installs it.
pitboard schedule install, and turning on Renew parked logins daily in the app,
writeALL_PROXY,HTTPS_PROXY,HTTP_PROXYandNO_PROXY, in whichever case each is
set, into the LaunchAgent'sEnvironmentVariablesor the systemd service's
Environment=lines, as they writePITBOARD_NO_ARGV. launchd and systemd start the
schedule without your shell's environment, so its requests went out directly, unless
they gave every job a proxy. Install it again after your proxy changes. On macOS,
pitboard schedule installrefuses a variable holding a character a LaunchAgent cannot
hold, such as the control character U+0001, and names it.pitboard doctorhas a
schedule_proxycheck, which warns when the schedule's proxy differs from the one the
run sees, with each user name and password as***. Where the run's proxy is a SOCKS
one that fails its requests, it says to change that variable before installing the
schedule again, which would give the schedule that proxy too. In the app's This Mac
pane the same line is not a warning: the app runs with the variables macOS gives it, not
your shell's, and it says that the app's switch would give the schedule the app's.
Changed
- A Windows build of Pitboard, from crates.io or from source, now compiles, for x64 and
ARM64 with Microsoft's toolchain. It saysPitboard for Windows is not released yet. This build changes nothing., with the codewindows_not_releasedin--json, and reads and
changes nothing: only--version,--help,completionsandmanpageanswer. Until now
it stopped compiling with the first of those sentences. On Windows,
cargo binstall pitboardfinds no release to download and stops, rather than build this
one from source. macOS and Linux are unchanged. - The product is called Pitboard, with a capital P, everywhere it names itself: the app
(its menus, About, Login Items and the Share menu entry, Pitboard and Pitboard
Debug), the command line's messages and help, and the docs. The command is still
pitboard, and nothing it reads, writes or prints as a key changes. Thehomecheck of
pitboard doctor --jsonis now namedPitboard home; its code is stillhome. pitboard statusnames a limit whose length is not a whole number of hours by its
minutes or seconds, as the app does, such as90mor45s. It showed the limit's kind
instead, such as90_minute, a name Pitboard builds from the length of a Codex limit.
pitboard status --jsonstill gives the kind.- The app says when a limit resets the way
pitboard statusdoes: resets in 2h 05m,
with the minutes in two digits, and resetting now once that time has come. It said
in 2h 5m, and nothing once the time had passed, so a limit whose reset was due showed
its old figure with nothing beside it. VoiceOver says resetting now too. - The app says how long an account lasts with the spans
pitboard statususes, such as
About 1h 05m left at this rate or Resets in 3d 2h, where it said About 1h 5m
left at this rate or Resets in 3d 2h 5m. Under a minute,pitboard statussays
about to run outorresets any moment, as the app does, where it said
about <1m left at this rateorresets in <1m. pitboard doctorand the app's This Mac pane sum up the checks in the same words:
Everything Pitboard checks is in order.when every check holds,
One thing is worth looking at.or2 things are worth looking at.when checks only
warn, and1 broken: do not switch accounts until fixed.when a check fails.doctor
saidEverything Pitboard relies on holds.or2 to look at; nothing is broken., and the
app counted a check that fails as one more thing worth looking at.pitboard renewand the app's Renew Now report a run in the same words:
No parked login was due.,Renewed one.,Renewed all 2.or
Renewed 1 of 2; the rest are tried again next time.renewsaidRenewed 1.or
Renewed 2.when it renewed everything due, and the app saidNothing was due.or
Renewed 1 of 2.- The command line runs the
claudeorcodexthatPITBOARD_CLAUDEorPITBOARD_CODEX
names, as the app does, to sign in, andpitboard doctorreports that program's build.
Before, only the app read them, and the command line always looked on itsPATH. Set
but empty, either names nothing, and the program is looked for onPATHas before. - In the app, an empty
PITBOARD_CLAUDEorPITBOARD_CODEXnames nothing too, and the app
looks for the program as it does when neither is set. Before, it offered the tool, then
refused its sign-in because the program was not installed. - Pitboard checks a link shared with it, or carried in a
pitboard://link, in its core
now, which changes three rare answers:- A site's host followed by
/,?or#and then a combining mark or a joiner, such
aschat.com/and an accent, is a link to that site. Pitboard took the mark and the
character before it as one, and found no link. - A link is too long past 8,192 Unicode scalars, where it was past 8,192 characters as
they show on screen. A link of letters each followed by a combining accent is too long
at about half the length it was. - A host written partly in full-width letters and partly in escapes, such as
c%EF%BD%8Caude.ai, is claude.ai, as the WHATWG URL standard maps it. Pitboard said
it was another site.
- A site's host followed by
- The app keeps its own preferences, the tools you said Not Now to a second account for
and whether it has opened its window on a first launch, inapp.jsonin Pitboard's
directory, so they followPITBOARD_HOMEas your accounts do. It kept one set in its
macOS preferences for every Pitboard directory, so a Not Now said with one
PITBOARD_HOMEheld with another. The first time it opens a directory with noapp.json,
it takes what its macOS preferences held, once. - A notification that an account in use has run out is posted once for each reset of that
limit, however often the app is quit and opened again meanwhile: what was posted is kept
intold.jsonin Pitboard's directory. The app posted it again after every relaunch. The
window still says it after a relaunch, as before. - What the app says that depends only on the time, such as used up until 14:05 in the
menu, is worked out once a minute, so a menu opened between two minutes can say it for up
to a minute after that time, as its bars already could. It was worked out as the menu
opened. - The Activity list names a switch Switch, from either front end. It named one
Use, afterpitboard use, the command the log records a switch by. - The settings learn whether this copy of the app can turn on daily renewal, or link its
command line, when they read the schedule or look for the command line, which they do
each time they are shown. They asked the file system again each time they drew, so a copy
moved meanwhile showed the new answer at once. - The app keeps the account windows' records, the data folders it made for each Pitboard
directory and the page each window was last on, inwindows.jsonin
~/Library/Application Support/com.usepitboard.Pitboard, private to you. It kept them in
its macOS preferences, aswebStoresandwindowPages. The first time it opens without
windows.json, it takes what those held, once, and removes them once the file is there.
Wherewindows.jsonis there and the app cannot read it, or cannot read records in it, as
after a hand edit, the app deletes no window's data and never writes over the file. Each
window then opens at its site's home with the sign-in note at each launch, and keeps its
data. - Building Pitboard on Windows, as
cargo install pitboarddoes there, stops with
Pitboard for Windows is not released yet.It said that Pitboard runs on macOS and
Linux, and that another system needs a host of its own. Pitboard still does not build on
Windows. - Pitboard's facts about Claude Code and Codex say, for each of macOS, Linux and Windows,
whether they were read there and from which build. Read from the Windows builds, x64 and
ARM64, seven of Claude Code's 17 facts hold on 2.1.289, and nine of Codex's 16 on
0.160.0. Three of Claude Code's are not read on Windows: two are about the keychain, and
the Windows build's Credential Manager store calls theBun.secretsthe third rules out.
Seven of each tool's facts wait on the Windows work. Where Claude Code finds its config
file was read again, from 2.1.289 on every system. The conformance check reads both
tools' Windows builds, and Codex's macOS build as well as its Linux one. What Pitboard
does is unchanged. - In
pitboard-core,assumptions::PlatformgainsWindows, andPlatform::ALL, which
meant macOS and Linux, is gone.assumptions::read_onreturns aVec<Platform>in place
of a&'static [Platform], andassumptions::verified_againsttakes the system as well
as the tool.assumptions::OnSystem,PerSystem,per_system,on,verified_onand
pendingare new. These change the crate's public API. - The JSON that
--jsonprints is ASCII alone, on every system. Each character outside
ASCII, in a label or an email address for example, is written as a\uescape: the label
Đạtis written\u0110\u1ea1t, and a character beyond U+FFFF as its UTF-16 surrogate
pair. A JSON parser reads the same values as before. Pitboard wrote such characters as
UTF-8, which a program that decodes the output in another code page read as other
characters. This reaches every output that carries such a character, whatever your labels
are: eachpitboard doctor --jsondetail joins its parts with·, and
pitboard statusline --json'slinedoes too, so both now carry\u00b7. What Pitboard
prints without--json, and the JSON files Pitboard keeps, are unchanged. - On macOS and Linux, Pitboard refuses every command that changes something when it runs
as root or withsudo, with the error codeelevated:
Pitboard changes nothing when it runs as root or with sudo. Run it as yourself.That
includes a container or a WSL distribution whose only user is root. Pitboard tells
sudobySUDO_UIDbeing set, as it is undersudo -uanother user too, and in a
shell, tmux or editor server started withsudo -iuand your own name, which passes
SUDO_UIDto everything it starts;env -u SUDO_UIDruns a command without it. Pitboard
changed things as root before: a file it made then was root's, and a keychain item might
have been, where your own runs might not read or replace it. A refused command writes
nothing, not even its line inpitboard log.pitboard schedule uninstalland
pitboard uninstallare refused too, so a schedule or a Pitboard directory made as root
is removed by hand, as
Pitboard changes nothing as root
says.pitboard statusstill answers, with whatpitboard status --offlineshows and the
warningread_only, which says why. It renews no parked login, asks Anthropic and
OpenAI nothing and writes nothing. The status line still answers too, and writes
nothing.pitboard doctorfails itselevatedcheck, first, with how Pitboard runs.pitboard renew --jsonreports a refused run as the errorelevated, where it gave
an empty list, and Renew Now in the app says why, where it said
No parked login was due.forgetanduninstallask no question whose answer would be refused, and
enroll --sign-indoes not say it is opening a sign-in.
- In
pitboard-core,service::Pitboard::renewreturns aResult, and
service::PermitandPitboard::permitare new. Everything outsideservicethat
writes, removes, changes a store of logins or the scheduler, starts a tool's sign-in or
renews a parked login takes aPermit, which onlyPitboard::permitmakes: among them
app::write_file,audit::record,schedule::install,status::gather,
switch::settleandstatusline::read, which takes anOption<Permit>and writes
nothing without one.host::Elevationis new. These change the crate's public API. pitboard schedule install, and turning on Renew parked logins daily in the app's
settings, refuse whilePITBOARD_HOMEnames a directory other than~/.pitboard, with
the error codeschedule_not_default_home, and install nothing. The schedule renews the
parked logins in~/.pitboardalone, so it would leave those in that directory to run
out. Both installed it all the same, and it renewed~/.pitboard. To turn on daily
renewal, unsetPITBOARD_HOME. To renew the parked logins in another directory, run
pitboard renewwithPITBOARD_HOMEset.pitboard schedule uninstall, and turning
the switch off in the app, still remove the schedule whateverPITBOARD_HOMEsays.
Pitboard compares the two as paths, so aPITBOARD_HOMEthat is a link to
~/.pitboardis refused too. WhilePITBOARD_HOMEnames another directory,
pitboard schedule statusadds that the schedule renews only the parked logins in
~/.pitboard, and with nothing installed it says to unsetPITBOARD_HOMEbefore
pitboard schedule install. It said to runpitboard schedule install, which is
refused there.- Pitboard refuses every SOCKS proxy:
socks4://,socks4a://,socks5://,socks5h://
andsocks://, which issocks5://. Pitboard takes its proxy from the first of
ALL_PROXY,HTTPS_PROXYandHTTP_PROXY, each read before its lower-case spelling,
that holds an address it can read. While that variable names a SOCKS proxy, each request
to Anthropic or OpenAI fails before anything is sent, with the causeunreachableand
the reasonPitboard does not use SOCKS proxies yet, and ALL_PROXY names one, naming
that variable. Requests with asocks4://,socks5://orsocks://proxy no longer go
out directly, around the proxy you set. Requests with asocks4a://orsocks5h://one
failed withConnection refused, and fail with that reason instead. A hostNO_PROXY
names is still reached directly, without the proxy's own name being looked up, and a
redirect from it is not followed while a SOCKS proxy is named. Pitboard does not go
through SOCKS proxies because ureq 3.4.2, the library it sends requests with, waits past
the request's 5 seconds on one that never answers, and looks the proxy up before it asks
NO_PROXY. - Pitboard writes the daily renewal schedule's files, the LaunchAgent and the systemd
service and timer, so that only you can read them, mode 600, whatever mode a file
already there had. They can hold a proxy's user name and password. A file Pitboard wrote
where none was there was already 600, and one already there kept its own mode, such as
644. - In
pitboard-core,service::Pitboard::check_homesand
service::Pitboard::schedule_renews_this_home, the errorsError::HomeNotAbsoluteand
Error::ScheduleNotDefaultHome,Context::started_by_the_scheduleand
schedule::SCHEDULED_RUNare new, andContext::with_claude_config_dirkeeps an empty
value, which it dropped as unset. - Pitboard's own Codex sign-ins, the ones
pitboard enroll codex/<label> --sign-inand the
app's Add Account and Sign In Again run, start Codex as
codex -c cli_auth_credentials_store="file" login, so Codex keeps the new login in
auth.jsonin the private folder Pitboard reads it back from./etc/codex/config.toml,
or a trusted project's.codex/config.toml, could have sent it to the keychain instead.
Only/etc/codex/managed_config.toml, a managed preference or a requirement is over the
-c. Where one of them chooses a store other than the file, Pitboard refuses the sign-in
before it starts, as the first entry under Fixed says; where one chooses the file, the
sign-in goes ahead. pitboard doctor'sCodex login storeline, and the app's This Mac pane, say which
setting chose Codex's store, such asfile (Codex's default)or
keyring (pinned to `keyring` by /etc/codex/requirements.toml). Where the store is the
file,doctoradds that Pitboard does not read a project's own.codex/config.toml. It
saidfile, or, whichever file chose another store,
keyring (cli_auth_credentials_store in config.toml).pitboard doctor --jsongives
unknownas Codex'sbackendwhere nobody can tell, as the second entry under Fixed
says.- In
pitboard-core,doctor::CodexFactshas two more fields,backend_settingand
backend_remedy. This changes the crate's public API. - In
pitboard-core,holder::Remedy::ReopenAppnames its app byapp, a
holder::AppId, in place ofbundle_id.AppId::MacBundleholds a Mac app's bundle id,
andAppId::as_strgives the id as its system writes it.holder::AppId,
service::Warning::SessionsUnknownandservice::Warning::SessionsUnknownAfterSignInare
new. These change the crate's public API. - Pitboard reads
ALL_PROXY,HTTPS_PROXY,HTTP_PROXYandNO_PROXY, and their
lower-case spellings, from the environment it was started with, by the rules ureq 3.4.2
reads them with, and gives ureq the proxy. ureq read them itself, from the process, so
each request goes through the same HTTP or HTTPS proxy as before. Inpitboard-core, a
context made withContext::newnames no proxy, where its requests went through the one
the process's environment named; a context read from an environment, as the command
line's and the app's are, goes through the one that environment names.doctor::Facts
has the fieldsnetworkandin_the_app,doctor::ScheduleFacthas the fields
networkandnetwork_differs, anddoctor::NetworkFactanddoctor::ProxyFactare
new. These change the crate's public API. - In
pitboard-core, theautoswitchmodule,service::Pitboard::auto_switch,
words::share_of_limitandwords::not_switchingare new, anderror::Errorhas the
variantSwitchOvertaken. These change the crate's public API.
Fixed
-
When the account in use runs out of a limit, the app's notification and window offer to
switch only to an account with room in every limit it reports, counting a limit whose
reset time has passed as reset. They offered the account with the most room in the limit
that ran out, even one that had run out of another limit. An account with five-hour room
and its weekly limit spent stopped at once when switched to. -
Where Pitboard cannot read the list of running processes, a Codex switch warns, with the
codesessions_unknown, that Pitboard could not tell whether Codex sessions started
before it are still running, and says not to sign out in one: that revokes the login
Pitboard has just parked. It said nothing, as when nothing runs Codex. In the app, the
switch's notice says it. -
Where Pitboard cannot read the list of running processes, a sign-in that puts a new login
of a Codex account in use warns withsessions_unknowntoo, and says to quit and start
again any Codex session still on the old login. Otherwise one of them can put the old
login back when it refreshes. It said nothing. In the app, the notice that the account
has a new login says it, and says it once where the last switch already could not tell. -
On macOS and Linux, Pitboard reads which store Codex keeps its login in from every layer
Codex 0.160.0 reads outside a project, in Codex's order: its default, the file store;
/etc/codex/config.toml;config.tomlin Codex's home;/etc/codex/managed_config.toml;
and on macOS the managed preferenceconfig_toml_base64ofcom.openai.codex, which a
configuration profile forces./etc/codex/requirements.toml, and on macOS the managed
preferencerequirements_toml_base64, pin the store over all of them. Pitboard read only
config.tomlin Codex's home, so on a machine where another of them put Codex's login in
the keychain or in memory, it looked for the login in anauth.jsonCodex does not keep:
it said nothing was signed in to Codex, and a sign-in it ran left its login where Pitboard
could not read it back. A Codex switch, enrolment or sign-in on such a machine is refused
withlive_store_unsupported, as one already was where Codex's ownconfig.tomlnamed
such a store, and the message names the setting and what would choose the file store: the line
cli_auth_credentials_store = "file"in your ownconfig.toml, or, where a requirement,
managed_config.tomlor a managed preference chose it, that only an administrator can
change it. -
Where one of those layers is there and Codex cannot read it, because it cannot be read,
is not TOML or names a store Codex does not have, Pitboard refuses with
live_store_unsupportedand says which, andpitboard doctorsays it cannot tell where
the login is: Codex 0.160.0 does not start then. Pitboard took such a store to be the
file.cli_auth_credentials_store = "nonsense"in your ownconfig.tomlwas read as the
file store, and is refused the same way. -
Where one of those layers has a
profile = "<name>"line, Pitboard reads it as choosing
the profile that a[profiles.<name>]table in any of them defines, as an older Codex,
such as 0.99.0, does. A profile's table does not change the store, since neither Codex
0.99.0 nor 0.160.0 readscli_auth_credentials_storethere, so the store is still the
one the layers choose. A line that names a profile no table defines is refused with
live_store_unsupported, as 0.99.0 refuses it; Pitboard ignored the line. Codex 0.160.0
does not start with such a line at all, whatever it names: it chooses a profile only
with--profile. -
On macOS and Linux, Pitboard no longer refuses a Codex account where your own
config.tomlsetscli_auth_credentials_storetokeyring,autoorephemeraland
/etc/codex/managed_config.toml,/etc/codex/requirements.tomlor, on macOS, a managed
preference sets it tofile. Codex 0.160.0 takes their setting over yours and keeps its
login inauth.json, so Pitboard parks and switches it there. Pitboard read only your
ownconfig.toml, and refused such an account withlive_store_unsupported. -
An account's window no longer opens again on a sign-in page of its site, such as
chatgpt.com's/api/auth, which would repeat a sign-in that has ended: it opens on the
page it showed before, or at the site's home. A window closed, or Pitboard quit, while it
was on such a page opened there again. -
After Claude Code refuses a code you paste, the sign-in sheet says Claude Code didn’t
take that code. Copy the whole code your browser shows, and paste it again. and offers
the code field again, and the same sign-in takes the next code. The sheet asked for a code
once, so a refused one left a sign-in that only Cancel could end. -
In the app, Sign In pressed right after Cancel no longer says another
pitboard enroll --sign-inis already waiting: the new sign-in starts once the one
cancelled has stopped. 0.7.0 stopped the cancelled sign-in in the background and started
the new one at once, which could find the old one still holding the lock that allows one
sign-in at a time. -
After you add, name, rename or forget an account, give up on an interrupted switch or
press Renew Now, the app shows what it reads of your accounts right after. When its
look for changes made in a terminal found the account index just as the app had written
it, the app took its own change for one made elsewhere and threw that read away: in an
open window, an account just added showed no numbers, and nothing said when the accounts
were read, until you opened the menu or went back to Accounts, either of which reads
at once, or the read the app makes every five minutes came. 0.7.0 did the same. A look
that found a rename before the rename had finished also put away what that account's
last switch said, and could notify you again that the account had run out. -
A sign-in that fails after its sheet has closed is said in the window. It was said
nowhere. -
A name the Name or Rename sheet could not save, once the sheet had closed or
another had taken its place, is said in the window. It was said nowhere. -
Quitting Pitboard stops a sign-in under way, and the tool's own sign-in with it, before the
app goes. Pitboard left aclaudeorcodexthat was signing in running after it quit. -
Two warnings with the same words are two notices in the window. They shared one id, which
a SwiftUI list does not allow, so what the window drew of them was not defined. -
This Mac lists the check of each account's parked login as a row of its own. Every
Claude Code account's check shared one id, and so did every Codex account's, which a
SwiftUI list does not allow, so what the pane drew of them was not defined. -
This Mac says it is checking until the last check asked for is in. With two under
way, after you left This Mac and came back while a check ran, it showed Checked
at once the first was in, beside checks still to come. -
After an interrupted switch Pitboard cannot finish, the app shows An interrupted switch
is waiting, with its Give Up button, each time it reads your accounts, and
pitboard statuswarns withrecovery_undeterminedin the words a change stops with.
Neither said so before. The app looks for it in what a read says, and no read said it, so
the notice never appeared and a switch failed with nothing offered but OK. Reading
finishes and changes nothing.pitboard status --offlineandpitboard doctorgive the
same message unless only Anthropic can tell whose login Claude Code is using. The app
puts the notice away once the switch is given up on or finished in a terminal. -
pitboard abandon, and the app's Give Up, give up on an interrupted Codex switch
whileCLAUDE_CODE_CUSTOM_OAUTH_URLis set, as a change to a Codex account goes ahead
with it set. Both refused, so a Codex switch nothing could finish could not be given up
on either. -
pitboard doctor, and the app's checks, no longer warn that a parked login "could not be
read this time" after a switch, a sign-in that enrolled an account, or a renewal of a
parked login. Pitboard writes a park's name down before it writes the login, and the name
stayed on that list until the next change, so doctor counted a login it had already
recorded. -
pitboard doctor's advice about a login others on the machine can read is one
sentence again, without a run of spaces in the middle of it. -
The note under the sign-in sheet's Code field says what Claude Code does: it takes
the code shown after you sign in, whether or not your browser came back to it. The note
said Claude Code asked for the code only when your browser could not reach it. -
The sign-in sheet links the right address when Claude Code prints it as a terminal
hyperlink. Claude Code does that when the app runs withFORCE_HYPERLINKset, or with
the variables a terminal such as iTerm2 sets. The link's address picked up a stray
control character, and sometimes a second copy of the address. -
With
HOMEunset, the command line used the folder it was run from as your home: it kept
its files in a.pitboardthere, and looked for Claude Code's and Codex's beside it. It
now uses your account's home directory, as the app does. -
With
HOMEorPITBOARD_HOMEempty, or with it,CLAUDE_CONFIG_DIR,
CLAUDE_SECURESTORAGE_CONFIG_DIRorCODEX_HOMEset to a path that does not start at
the root, such aspitboard, Pitboard refuses every command butcompletions,
manpageanddoctorwith the error codehome_not_absolute, which names the variable:
HOME is empty, so the folder it names would depend on where each program runs. Set it to a full path, or unset it; Pitboard reads and changes nothing until then.Pitboard
took such a path to be under the folder it was run from: an emptyHOMEor
PITBOARD_HOMEkept its files there, and an emptyHOMEhad
pitboard schedule installwrite the schedule's file there. Claude Code and Codex take a
relative one from the folder each of them runs in, so the login Pitboard read and
switched was the one under the folder Pitboard ran in.pitboard doctorfails ahomes
check instead, and checks nothing else; the app's This Mac pane shows the same. The
app reads nothing under such a home either. It shows the error where it shows your
accounts, finds no tool and nopitboard, and leaves its own files there unread and
unwritten. -
A run of the daily renewal schedule renews the parked logins in
~/.pitboardwhatever
PITBOARD_HOMEsays, empty or relative included, which other commands refuse as above.
The schedule renews~/.pitboardalone, and its job is written without
PITBOARD_HOME, but aPITBOARD_HOMEset withlaunchctl setenvon macOS, or in
systemd's user environment on Linux, throughsystemctl --user set-environmentor
environment.d, reached the job. The job then renewed that directory's parked logins
and left those in~/.pitboardto run out. On macOS Pitboard tells a run of the
schedule by the job's name, which launchd gives it inXPC_SERVICE_NAME. On Linux it
does not rely on what systemd passes a job, which systemd itself calls best effort, so
the timer's service runspitboard renew --scheduled. A timer installed by an earlier
Pitboard runspitboard renew, which renews the directoryPITBOARD_HOMEnames as
before, until you runpitboard schedule installagain, which rewrites it. If you go
back to an earlier Pitboard on Linux, run itspitboard schedule installas well: an
earlier Pitboard does not take--scheduled, so until then the timer fails every day. -
With
CLAUDE_CONFIG_DIRset but empty, Pitboard refuses as above, with
home_not_absolute:CLAUDE_CONFIG_DIR is empty, so the folder it names would depend on where each program runs.Claude Code reads an empty one as unset for~/.claude.json
and for which keychain item holds its login, but as the folder it runs in for its
config directory, so its settings, the lock around its login and, on Linux, the login in
.credentials.jsonare in whatever folder each session started in. Pitboard read an
empty one as unset, so it took that lock, and on Linux switched that login, in
~/.claude, where no Claude Code started with it looks. On macOS such a switch did move
the login, which is in the default keychain item, and took only the lock in the wrong
folder; it is refused all the same. To keep switching, unsetCLAUDE_CONFIG_DIR. -
The command line takes a
claudeorcodexto be the tool's program only when it is a
regular file you may run, as the system judges it when it starts one, and otherwise looks
further onPATH. It took a file only others may run, and then failed to start it. -
The app reads the environment it was started with as the command line reads its own, by
the same code. It read only the variables that move where things are kept, and ignored
these, which it now reads as the command line does:CLAUDE_CODE_CUSTOM_OAUTH_URL: while it is set, the app refuses changes to Claude Code
accounts.- The variables that make Claude Code sign in another way, such as
ANTHROPIC_API_KEY:
the app's checks and changes warn about one that is set. CLAUDE_CODE_HOVER_REST, which switches on Claude Code's successor credential
backend: the app's checks say it is on, and warn when the login is in the plaintext
file, where what Pitboard reads may not be what Claude Code reads.PITBOARD_API_BASE, a hook for Pitboard's tests: the app sends the requests meant for
Anthropic and OpenAI to the loopback address it names, and ignores any other address.
An app opened from Finder has these only when they are set with
launchctl setenv. Both
front ends already read the first two from Claude Code's settings files. -
The app takes a
claudeorcodexit finds to be the tool's program only when it is a
regular file you may run, as the command line does, and otherwise looks further. It took
a directory with that name, offered the tool, and failed to start its sign-in. -
The Open Link window and the Share sheet name claude.ai and chatgpt.com in English, as
the rest of what they say is. On a Mac set to another language they joined the two names
in that language, as inThere is no claude.aiまたはchatgpt.com link in what was shared.
Security
- Pitboard refuses more folders that sync to other machines as its directory, with
state_on_synced_drive: any folder inside~/Library/CloudStorage, where macOS keeps
the folders of cloud storage apps such as Box and Google Drive, or inside
~/Library/Mobile Documents, where iCloud Drive keeps each app's folder; and any folder
namedDropbox,Dropbox (<team>),Google Drive,GoogleDrive-<account>,
OneDrive,OneDrive - <organisation>,OneDrive-<kind>,com~apple~CloudDocsor
Sync. A name counts in any case, so~/dropboxis refused as~/Dropboxwas, and
wherever it is in the path, your home's own folders included: with a home of
/home/syncor/home/dropbox,~/.pitboardis refused though nothing syncs it, and
PITBOARD_HOMEcan name a folder outside it. On Linux the parked logins are files in
that directory, which such a folder would copy to every machine it syncs to. Every
folder Pitboard refused before is still refused,SyncAdminandOneDriveToolsamong
them, though they sync nothing. - Pitboard refuses a site's sign-in link from outside however its path is written. It took
claude.ai/magic-link/%FFandchatgpt.com/api/auth/%C3, whose paths hold a byte that is
not text, andclaude.ai/magic-link/%CC%81, whose path has a combining mark after a
slash, for pages of the site, and offered to open them in an account's window. Somebody
else's sign-in link opened there would sign the window in as them, under your label. A
link that names a port or a user is refused whatever they hold: one whose port is too
long for a number, or whose user name is not text, was opened without them. - Pitboard refuses a link whose path has a
.or..segment whatever else the path
holds. It openedclaude.ai/x/../%FF, whose path holds a byte that is not text, though
WebKit drops the dot segments and loads another page than the link reads. - A
pitboard://link that names a user or a port, or has a path, is one Pitboard cannot
read whatever they hold. One whose user, port or path is not text, such as
pitboard://open/%FF?url=claude.ai, was read as if it had none.