Problem
When a refresh grant fails with invalid_grant, rotate_token revokes the subscription. The comment on that path says "the UI shows Reconnect" — but the UI cannot show it: VaultSecret._list filters revoked_at IS NULL, so GET /api/providers/subscriptions omits the revoked row and the Providers page renders no card for that provider at all.
The result is indistinguishable from never having connected the provider. The Usage page says only "not connected". The reason (revoked_reason), the time (revoked_at), and the account identity sit unread in the vault table. The operator's first theory is a druks bug, and the diagnosis needs a database query.
Observed: the nightly refresh tick presented a refresh token, the provider answered invalid_grant, the row was revoked at 03:30 — correct behavior. The next morning the provider card was simply gone, with agents on that subscription failing at seat resolution.
Proposal
Surface the revocation instead of hiding the row. The data is already persisted: revoke() keeps revoked_at, revoked_reason, and identity, and wipes only the secrets.
list_subscriptions also returns the latest revoked row per provider and account — as a status: connected | revoked field rather than omission.
- The card renders it: "Disconnected — the provider rejected the stored credential (
invalid_grant) on . Reconnect to restore." with the Reconnect button.
- A fresh connect supersedes the revoked row, as it already does.
The revocation behavior itself stays as is — dropping the dead lineage so the next tick has no row to hammer is right.
Related gap: no rotation history
Post-mortem on a dead grant is nearly blind today. created_at is stamped by whatever wrote the row (a refresh persist or a migration, not necessarily the operator's connect), and revoke() is the last write, so updated_at no longer shows the last successful refresh. Consider stamping the last successful rotation separately (one timestamp column or an identity field), so the surfaced card can say when the credential last worked.
Problem
When a refresh grant fails with
invalid_grant,rotate_tokenrevokes the subscription. The comment on that path says "the UI shows Reconnect" — but the UI cannot show it:VaultSecret._listfiltersrevoked_at IS NULL, soGET /api/providers/subscriptionsomits the revoked row and the Providers page renders no card for that provider at all.The result is indistinguishable from never having connected the provider. The Usage page says only "not connected". The reason (
revoked_reason), the time (revoked_at), and the account identity sit unread in the vault table. The operator's first theory is a druks bug, and the diagnosis needs a database query.Observed: the nightly refresh tick presented a refresh token, the provider answered
invalid_grant, the row was revoked at 03:30 — correct behavior. The next morning the provider card was simply gone, with agents on that subscription failing at seat resolution.Proposal
Surface the revocation instead of hiding the row. The data is already persisted:
revoke()keepsrevoked_at,revoked_reason, andidentity, and wipes only the secrets.list_subscriptionsalso returns the latest revoked row per provider and account — as astatus: connected | revokedfield rather than omission.invalid_grant) on . Reconnect to restore." with the Reconnect button.The revocation behavior itself stays as is — dropping the dead lineage so the next tick has no row to hammer is right.
Related gap: no rotation history
Post-mortem on a dead grant is nearly blind today.
created_atis stamped by whatever wrote the row (a refresh persist or a migration, not necessarily the operator's connect), andrevoke()is the last write, soupdated_atno longer shows the last successful refresh. Consider stamping the last successful rotation separately (one timestamp column or an identity field), so the surfaced card can say when the credential last worked.