← 04 TRACEWhat is left behind?
Trying to recover the token destroyed it
The keychain handed back hex, so I decoded it and stored it again. security's interactive prompt truncates at 128 characters, and the original was gone.
A 401. The response body was empty, so there was nothing to work from.
Probing the shape of the keychain value, the prefix was 41544.
41 54 41 54 54 = A T A T T
It was stored as hex. security find-generic-password -w prints hex instead of
plaintext when the stored value contains a newline.
The recovery was the destruction
I decoded it and stored it again, using security's interactive prompt.
That prompt truncates at 128 characters. A 192-character token became 128, and the original was overwritten. It had to be reissued.
Measured afterwards.
| How it was stored | 200 characters in → read back |
|---|---|
-w with no value (interactive prompt) | 128 characters |
-w '<VALUE>' as an argument | 200 characters |
I had written elsewhere that "the interactive prompt keeps the value out of argv, so it is
safer — recommended." That advice had never checked this limit.
The safer option was the one that silently truncated the data.
The real cause was the absence of a diagnostic
A configuration error showed up as a plain 401. With an empty response body, the trail led to picking apart a hex prefix by hand — and that is where the wrong recovery was attempted.
So bb_doctor exists. It detects all four traps encountered — a token with no scope, the
128-character truncation, hex from a newline, an empty allowlist — and hands back the
command to run.
### hex token → ok=false, 4 problems
✖ Token shape: looks hex-encoded (386 chars)
fix: security add-generic-password -U -s bb-api-token -a "$USER" -w '<TOKEN>'
✖ Auth: 401 — credentials rejected
The scope list is pulled from the granted array in the response body when /user returns
403. Rather than guessing from an empty body, it reads what the server already said.
The token value is never printed in any form. Not even a prefix — the check is a boolean, and
a test asserts the response does not contain the string ATATT at all.
What is left
Putting a token into the keychain briefly exposes the value in argv. That is unavoidable.
The interactive prompt removes the exposure but truncates at 128, and this token is over 190
characters. With no alternative, the exposed path was chosen and the fact written down.
The most dangerous moment for a recovery procedure is the first time it is used. Especially if the command overwrites the original.