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 stored200 characters in → read back
-w with no value (interactive prompt)128 characters
-w '<VALUE>' as an argument200 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.

No diagnosticNo scopeCut at 128Newline → hexEmpty allowlistJust a 401bb_doctorToken shape: looks hex-encoded (386 chars)Auth: 401 — credentials rejectedfix: hands you the command to runFour causes wearing one face turns diagnosis into guessing. The guess turned recovery into destruction.

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.

keychainmacosdiagnostics

Record

First committed 2026.09.03, and untouched since.

  • 747843eContent: bb-mcp 개발 기록을 두 편으로 옮겼다

The full build record →