Credential Prompts Are Side Effects

Credential prompts are side effects

A credential prompt is not a read. It is an interaction with the user's desktop.

That distinction matters most in daemon-backed apps. A background process can think it is only checking whether a token exists, but the OS can turn that check into a modal keychain prompt. If the user is away, every retry becomes another demand for attention.

The rule

Keep prompt-capable credential reads out of unattended startup and polling
paths. If a background process still discovers that it needs the user, that
fact becomes state.

Do not keep asking. Latch the auth-required state, notify once, fail fast, and wait for an explicit repair action such as login, reauth, or unlock.

What Spotuify taught us

spotuify used to have two separate auth lessons tangled together:

The refresh-token fix made token rotation safe. The prompt-storm fix made auth failure humane.

The current shape is better:

What mxr reinforced

In mxr 0.6.10, constructing a password-backed IMAP account eagerly read the
macOS Keychain. After a binary upgrade changed the ad-hoc signing identity, the
keychain asked for interactive approval. The daemon failed before its socket was
ready, so every client lost access because one account needed attention.

The 0.6.11 and 0.6.12 fixes changed the dependency, not just the error message:

The tradeoff is explicit: filesystem permissions replace OS encryption for
IMAP/SMTP passwords. In return, a binary upgrade cannot turn a background
startup read into a modal prompt that bricks the whole mail runtime.

The broader lesson is not that every app should copy this storage choice. It is
that an interactive credential backend should not be a global startup
dependency. One broken account should fail its own operation and leave the
daemon, diagnostics, other accounts, and repair path available.

The pattern

Use this for any local app that mixes a daemon, one-shot commands, and OS credentials:

  1. Put a non-interactive credential source in front of the interactive store.
  2. Resolve credentials at the narrow operation that needs them, not during global startup.
  3. Keep diagnostics and repair available when provider auth is broken.
  4. Bound any unavoidable credential-store read with a timeout.
  5. Classify "user approval needed" separately from network, provider, and parsing errors.
  6. Store that classification in the long-lived owner, usually the daemon.
  7. Deduplicate user-facing notifications by error kind.
  8. Make the repair path explicit and idempotent.

The key move is ownership. The daemon owns auth-required state because it owns
runtime truth. A CLI or TUI can display it, but it should not rediscover it by
poking the keychain again. If the daemon itself cannot start, the repair command
needs a smaller dependency path that can operate directly on configuration and
credential storage.

What not to do

Why this generalises

This applies to more than Spotify:

Any place where "check credentials" can show a prompt needs this rule.

Code evidence

In spotuify:

In mxr:

The useful lesson is not "never use the keychain." It is "never let a background loop repeatedly ask the user the same question."

See also