Skip to content

fix(team-vault): a failed secret save never passes for a saved one - #611

Merged
kipavy merged 1 commit into
devfrom
fix/team-secret-save-failures
Oct 11, 2026
Merged

kipavy merged 1 commit into
devfrom
fix/team-secret-save-failures

Conversation

@kipavy

@kipavy kipavy commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #609, for the two cases it left open (refs #608).

Problem

A team secret (password, key, passphrase, knock sequence) whose upload failed stayed in the in-memory cache as if the server held it. Reopening the editor showed it as saved, and the next vault load silently removed it. #609 fixed that only when the server is too old for the secret's type.

Change

The team secret cache now has two layers: what the server holds, and over it the values whose upload has not landed. A write shows its value from the second layer and moves it to the first only when the server has accepted it.

Server refused (any 4xx except 401, 408, 429 — so 400, 402, 403, 404, 413, 426...): the value is dropped immediately and the editor shows what the server holds again. The error reads "The team vault did not accept this, so it was not saved: …".

Server not reached (offline, timeout, 5xx, expired session, rate limit): the value stays usable for the session, survives vault reloads, and is uploaded again after every load of the team — foreground or background — until it lands or the app closes. The warning reads "Not uploaded to the team vault yet. It is kept until Voltius closes and retried the next time the vault loads: …".

Nothing is written to disk: the memory-only rule of #402 is untouched. Keys queued by a move into a team keep their existing on-device copy and sweep retry.

Also:

  • Clearing a secret drops its unsent value first, so a later retry cannot re-upload something the user removed.
  • The retry stops at the first value the server still cannot take, and drops any value the server now refuses (for example its host was deleted meanwhile).
  • Connect prompts, import, paste, duplicate, copy to vault, undo and plugin key creation used to only log these failures. They now raise one toast per distinct failure (keepCachedOnUploadFailure → reportUploadFailure). The opportunistic public-key backfill stays log-only.
  • Every failure logs one line with the outcome, the key name and the reason.

Declared trade-offs

  • An unsent value is lost if the app closes before the server is reachable again. The warning says so.
  • A retry is last-write-wins, like every other team vault write: it replaces a value a teammate saved in between. The window is one app session.
  • There is no lasting "not synced" marker on the host or in the editor, only the toast and the editor's error.
  • A member without edit rights who types a password at a connect prompt no longer has it kept for later reconnects in that session; the save is refused and says why.
  • Mobile edit screens still swallow every save error (unchanged; they have no error surface).

Tests

secretRouting.test.ts (refused vs unreachable by status, reload keeps the unsent value, retry, retry stops/drops, cleared-while-unsent), teamSecretCache.test.ts (layering), teamVaultSync.credentials.test.ts (foreground and background loads both retry). Full vitest suite and tsc pass locally. No live run against a server; toast text only, no screenshots.

A team secret whose upload failed stayed in the in-memory cache as if the
server held it, then vanished at the next vault load. #609 fixed that only for
a server too old for the secret's type.

The cache now keeps two layers: what the server holds, and over it the values
whose upload has not landed. A write shows its value from the second layer and
moves it to the first only once the server has accepted it.

- Refused (any 4xx except 401, 408 and 429): the value is dropped at once and
  the editor shows what the server holds again.
- Not reached (offline, timeout, 5xx, expired session): the value stays usable,
  survives vault reloads, and is uploaded again after every load of the team,
  foreground or background, until it lands or the app closes. Nothing is
  written to disk, so the memory-only rule of #402 holds. Clearing the secret
  drops the unsent value, so a later retry cannot bring it back.

Connect prompts, import, paste, duplicate, copy to vault, undo and plugin key
creation only logged these failures; they now raise one toast per distinct
failure. The opportunistic public-key backfill stays log-only.

Keys queued by a move into a team keep their on-device copy and their sweep
retry, as before.

Refs #608
@kipavy
kipavy merged commit f1ff9c0 into dev Oct 11, 2026
5 checks passed
@kipavy
kipavy deleted the fix/team-secret-save-failures branch October 11, 2026 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant