015 — tp unlock restarts the clock; tp unlock query prints the time left (#38)

Status: shipped (v0.10.0)

Why

tp unlock caches a Bitwarden session key for 24 hours from the moment the master password is typed, and a second run inside that window is a no-op that reports the time left. That no-op is the wrong default for the way the command is used: it is typed on purpose, usually before a stretch of work that needs the vault, and the person expects a fresh 24 hours, not "already cached, 2h left" followed by an expiry mid-session. The --refresh flag exists but has to be remembered.

Agents and scripts also need the session state without prose: today it is one line of tp check ("ok, 7h12m left") or the error of the first command that needs a token.

What changes

  • tp unlock always prompts for the master password (via bw, which reads it; tp never sees it) and caches the new key, so the 24 hours run from the last tp unlock. The --refresh flag goes: it is the behaviour now.
  • tp unlock query prints the time left as a Go duration rounded to the minute (7h12m) and nothing else on stdout, exit 0. With no cached session, an expired one, or a key the vault no longer accepts, it prints the reason on stderr and exits 1. Expiry has the side effect Session already has: the cached key is deleted and the vault locked.
  • tp check's secrets line and the tp unlock success message are unchanged; README.md and tp -a describe the new behaviour.

Acceptance

Cached session Command Prompts stdout exit
none tp unlock yes cached message 0
valid, 2h left tp unlock yes cached message; 24h left afterwards 0
valid, vault locked elsewhere tp unlock yes cached message 0
bw unlock fails or returns no key tp unlock yes error on stderr; cache untouched 1
valid, 7h12m left, vault accepts the key tp unlock query no 7h12m 0
none tp unlock query no nothing; "no cached Bitwarden session — run: tp unlock" on stderr 1
older than 24h tp unlock query no nothing; "expired" on stderr; file removed, vault locked 1
within 24h, vault rejects the key tp unlock query no nothing; "not accepted by the vault" on stderr 1
any tp unlock --refresh — flag unknown 2

Out of scope

  • Extending a session without the master password (touching the cache file): see Decisions.
  • A machine-readable form beyond the duration (JSON, seconds): the duration is what tp check prints and what a person reads.
  • Changing the 24h TTL or making it configurable.

Decisions

None recorded as an ADR. The fork was whether "restarts the clock" means re-authenticating (prompt, new key) or merely touching the cache file's mtime when the vault still accepts the key. Touching would make the 24h limit meaningless — a daily tp unlock would keep one session alive for ever without a password — so the clock restarts only with a new key. query verifies the key with the vault, one bw call, rather than trusting the file: a 0 exit means the next token fetch will work.

Done when

  • tests express the acceptance table and pass
  • spec updated (README.md)
  • roadmap ticked and stamped; changelog entry
  • issue closed with a link to the release (v0.10.0)