Skip to content

feat: erase all local data, and rebuild the vault database in place - #17

Merged
pyramation merged 2 commits into
mainfrom
feat/erase-all-and-rebuild
Aug 8, 2026
Merged

feat: erase all local data, and rebuild the vault database in place#17
pyramation merged 2 commits into
mainfrom
feat/erase-all-and-rebuild

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

Two Settings actions, both about the local database.

Erase all data (destructive, type ERASE to confirm) deletes everything dcrypt keeps on the machine — the whole appstash root, so the vault snapshot, keychain and identity file go with it — and drops the renderer back to the create-vault screen. The next "Create vault" re-runs the pgpm deploy exactly like a first run, so redeploying after a wipe needs nothing separate.

The subtlety is that erasing must not persist on the way out. lock() saves first, which would rewrite the file we are about to delete (or recreate it after deletion), so the vault grew a sibling:

async lock()    { if (!this.db) return; await this.save(); await this.discard(); }
async discard() { /* close + zero key material, no save */ }

VaultService.eraseAll() cancels the debounced save timer, awaits any in-flight lock, discard()s, then fs.rm(appDataPath(), { recursive: true }). Backups the user saved elsewhere are untouched.

Rebuild database re-runs the pgpm deploy into a fresh PGlite and moves every row across, for when a module version changes the schema under an existing vault. It copies at the row level rather than through the public API, so nothing is decrypted and nothing is re-keyed:

// value_enc travels as hex; the db_key_salt meta row comes along, so the
// same master password still opens the result
{ table: 'fields', columns: [..., 'value_enc', ...], binary: ['value_enc'],
  casts: { purpose: 'dcrypt_vault.field_purpose' } }

COPY_ORDER walks meta → folders → items → fields → password_history → tags → item_tags → urls → audit_log so foreign keys are satisfied as it goes; folders are inserted with parent_id null and re-attached in a second pass because of their self-reference. Item/field/folder ids are preserved, so a one-time code produces the same digits before and after. The old database is only closed once the copy succeeds — a failure closes the fresh one and leaves the live vault untouched.

Tests: rebuild() round-trips a nested folder, a favorite login with concealed and unconcealed fields, a URL, a tag and a TOTP seed (asserting the same code afterwards, and again after lock/reopen); discard() is asserted to leave post-save edits on the floor.

Link to Devin session: https://app.devin.ai/sessions/04636534e07048089ffb6b78142e12cd
Requested by: @pyramation

@pyramation pyramation self-assigned this Aug 7, 2026
@devin-ai-integration

Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 76ca5a1 into main Aug 8, 2026
5 checks passed
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