# Authentication leases

`crates/storage` owns the authentication and inactivity policy shared by every
interactive frontend. `AuthenticationSession` owns an uncached `SecretStore`,
and `authenticate` accepts only non-secret `KeyInfo`. A frontend therefore
never receives or retains a GPG passphrase itself.

An authenticated session returns an `AuthenticationHandle`. Every clone is
bound to the same generation. Manual lock, cancellation, or expiry revokes the
generation, clears its cached unlock material, locks the backing secret store,
and makes all old handles reject later secret access. A new authentication
creates a distinct generation, so an old editor or view cannot become valid
again accidentally.

The lease caches only a zeroizing `SecretBytes` passphrase for each protected
key actually requested during the active generation. The underlying
`SecretStore` cache is disabled for the session, avoiding duplicate cache
lifetimes. Relock drops this map before returning. Git credentials remain in
the OS-backed store and are retrieved only through a currently valid handle.

## Activity and expiry

The shared TOML setting is:

```toml
[security]
inactivity_timeout_seconds = 120
```

The default is 120 seconds; valid values range from 1 second through 24 hours.
Frontends call `touch_user_activity` only for real keyboard, pointer, touch, or
other intentional user input. Reading a secret, polling `remaining_time` or
`expire`, refreshing repository state, performing Git work, and repainting do
not move the deadline. This keeps presentation adapters from inventing their
own activity heuristics or timeout arithmetic.

All access checks and relock transitions share one operation lock. At the exact
deadline, either an operation finishes before relock or expiry wins and the
operation observes a revoked handle; there is no check-then-use window into the
secret store. Frontend timers may call `expire` to eagerly clean up at the
deadline, while every handle operation also checks expiry before accessing a
secret. The monotonic clock boundary is injectable so these cases remain fully
deterministic in tests.

OS authentication cancellation is reported distinctly. Explicit `cancel`
also revokes an active generation, which lets an abandoned authentication UI
clean up through the same storage-owned path as manual lock.
