Periodic credential token refresh + dedupe Keychain service names - #32
Periodic credential token refresh + dedupe Keychain service names#32mvrska wants to merge 6 commits into
Conversation
|
Thanks for this — the Keychain dedupe is clean and the auto-refresh concept directly addresses #6, which is a real pain point. Before we move forward on the token refresh part, a question: have you been able to verify that the refresh actually triggers successfully at the 30-minute threshold? Specifically:
We tested If you've confirmed it works end-to-end on your setup, that would give us the confidence to merge. The Keychain dedupe part we can cherry-pick regardless. |
|
Following this one closely. I was never able to get automatic token refresh to work as it constantly hit 429 errors. Plus my token needs to be refreshed every 8 hours which is certainly an annoyance. Especially when using OC on a headless machine. |
Hi, I was able to chat with openclaw today on multiple occasions since yesterdays restart of proxy.js. So either there was some claude code call that I am not aware of by openclaw, which refreshed the token, or it worked 🤷🏻♂️ |
The previous 30m threshold fired refreshes while Claude CLI still considered the token valid, so five consecutive "refreshes" per cycle were no-ops that just re-copied the same Keychain entry. Only the final attempt (when the token was nearly expired) actually rotated it. Drop the threshold to 2m and the check cadence to 15s so a refresh fires once, just before real expiry, instead of spamming misleading "refreshed" log lines. Also hoist the tunables and the sk-ant synthetic expiry into top-of-file constants.
Revert the default check cadence back to 5m. When a refresh attempt returns the same (or earlier) expiry -- meaning Claude CLI declined to actually rotate -- reschedule the next check 15s out instead of waiting another full interval. Successful rotations drop back to the normal 5m cadence. Switches from setInterval to a self-rescheduling setTimeout so the cadence can vary per-tick, and adds a refreshRetrySeconds config knob.
Read the current expiresAt and arm a single setTimeout to fire exactly when the token crosses the 2m threshold, instead of waking up every few minutes to check. After a no-op refresh we still retry every 15s until Claude CLI actually rotates. Removes the now-unused refreshCheckMinutes config key.
|
Just tested this manually myself using: claude -p "ping" --max-turns 1 --no-session-persistence Tested it every minute starting at 6 minutes remaining and checked /health. It failed up until my token had <2 minutes remaining and then it worked. So I'll probably have my agent create a job to refresh the token at 7 hours and 59 minutes. |
|
I can confirm again this morning from manual testing that using claude -p "ping" --max-turns 1 --no-session-persistence With <2 minutes remaining works to refresh token |
|
we used this internally on two different OC instances and seem to have no issue with tokens |
Would you mind sharing your exact setup as this is something I'd love to finally solve. The manual attempts I tried worked UNTIL Claude Code logged me out and I had to use 'Claude login' yesterday. Since then the manual attempts work again at <2 minutes. So I think the fix would have to notify if a login message is encounter. Not sure how often Claude Code requires you to login or why it logs you out. |


Summary
Two small improvements to credential handling:
Periodic token refresh (
proxy.js) — the proxy now checks token expiry every 5 minutes and, when less than 30 minutes remain, triggers a refresh by running `claude -p "ping" --max-turns 1 --no-session-persistence` and (on macOS) re-extracting from the Keychain into the snapshot file. Configurable via `refreshEnabled`, `refreshCheckMinutes`, `refreshThresholdMinutes` in `config.json`. Skipped automatically when running in `OAUTH_TOKEN` env-var mode. This avoids the ~24h manual refresh loop documented in the README.Dedupe Keychain service names — the list `['Claude Code-credentials', 'claude-code', 'claude', 'com.anthropic.claude-code']` was duplicated across `proxy.js`, `setup.js`, and `troubleshoot.js` (sometimes multiple times per file in both the loop and the log messages). Each file now defines a single `KEYCHAIN_SERVICES` constant at the top, and log messages derive from it via `.join(', ')`.