Build Enpass

KINDAreplaces $1.99/mosaves $23.88/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A local encrypted vault whose two hard problems are handling keys correctly using primitives you did not invent, and never losing data across writes, sync conflicts and format changes over years of use.

estimated effort multi-daythe files for this build are in the project pack

CryptographyA maintained library with published parameters, no custom constructionsKey derivationArgon2id at the current published parameters, recorded per vaultStorageA versioned file format written atomicallySyncThrough storage the user controls, with conflicts kept rather than resolvedRuntimeA local application; the vault never leaves the machine unencrypted

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the local application.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why Every primitive comes from here, and choosing an unmaintained or hand-rolled implementation is the single worst decision available in this project.

    Get it A well-reviewed library for your runtime providing Argon2id and an authenticated encryption mode.

    Verify It produces the published test vectors for both

  3. have readyfree

    Why The derivation parameters are copied from it rather than chosen, and they change, so the version you followed needs recording.

    Get it Read the current guidance, note the recommended parameters and the date, and store both in your documentation. open ↗

    Verify A written note records the parameters and the guidance version followed

  4. have readypennies per month, or free with existing sync

    Why The whole premise is that the vault syncs through the user's own storage rather than a service you operate, which changes the trust model entirely.

    Get it Any file sync you already use, or object storage with a key you hold.

    Verify A file written on one device appears on another

  5. have readyfree

    Why Conflict handling is the part most likely to lose data and it is only testable with two devices editing offline simultaneously.

    Get it A second machine or a virtual machine with its own device identifier.

    Verify Both can edit offline and reconcile against the same remote

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- vault_file(format_version, kdf_name, kdf_params_json, salt, nonce, ciphertext, mac) · everything needed to open it later
- entries(id, kind, title, fields_json, tags_json, created_at, updated_at, deleted_at) · inside the encrypted payload
- history(entry_id, ordinal, fields_json, at) · previous values, because a changed password sometimes needs the old one
- attachments(entry_id, id, filename, bytes, blob_ref) · encrypted with the vault, size bounded
- sync_state(remote_ref, last_seen_version, last_pushed_version, device_id)
- conflicts(id, remote_version, local_version, detected_at, resolved_at, resolution) · both sides retained
- The key derivation parameters are stored in the file, so a vault written under old parameters still opens after the recommendations change.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
VAULT_PATHrequired~/.local/share/vault/vault.encThe encrypted vault. Everything you have, so back it up.
KDF_PARAMSrequiredargon2id:m=47104,t=1,p=1Copied from current guidance and written into each vault it produces.
BACKUP_DIRrequired~/.local/share/vault/backupsAutomatic generations. The cheapest insurance in this category.
BACKUP_KEEPrequired10How many previous vaults to retain. Encrypted, so keeping several is safe.
CLIPBOARD_CLEAR_SECONDSrequired30After copying a credential. Verified to have cleared.
LOCK_IDLE_MINUTESrequired5Also locks on sleep. An unlocked vault in memory is the exposure.

The build, in order

  1. A vault you can still open next year

    Encryption uses vetted primitives and the file records how to decrypt it.

    1. Copied from the guidance, stored in the file, with the version noted.

      Files src/crypto.tsdocs/crypto-parameters.md

    2. Tampering should fail loudly rather than decrypt to nonsense.

      Files src/crypto.ts

    3. Never to disk, never to a log, and zeroed when locked.

      Files src/session.tstests/no-secret-leak.test.ts

    done when · tick each as it passes
    watch out
    • Hard-coding the derivation parameters means a vault written today cannot be opened once you raise them, unless you also keep every past value in code forever. Store the parameters in the file, so opening reads what was used and upgrading rewrites with the new ones on the next unlock.
  2. Writes that cannot half happen

    A crash mid-save never leaves an unreadable vault.

    1. Verify by opening the new file before it becomes the vault.

      Files src/storage.ts

    2. The cheapest insurance against every failure in this category.

      Files src/backups.ts

    3. A backup nobody has restored is a hope.

      Files tests/restore.test.ts

    done when · tick each as it passes
    watch out
    • Writing over the vault in place means a crash, a full disk or a battery dying mid-write leaves a truncated file and every password gone, and this loses more vaults than attackers do. Write elsewhere, verify it opens, then replace, and keep the previous generation.
  3. Entries, history and the awkward fields

    A vault holds real credentials including the ones that do not fit a template.

    1. A changed password sometimes needs to be changed back.

      Files src/history.ts

    2. A plaintext search index defeats the encryption entirely.

      Files src/search.tstests/no-plaintext-index.test.ts

    3. Time-based codes are easy to get subtly wrong.

      Files src/otp.tssrc/otp.test.ts

    done when · tick each as it passes
    watch out
    • Overwriting a password without keeping the old one means a site that rejects the change, or a device still using the old credential, leaves the user locked out with no way back. Keep history, and make it easy to see the previous value.
  4. Sync that never picks a winner

    Two devices editing the vault do not lose an entry.

    1. Not by timestamp. Clocks disagree and the loser is a password.

      Files src/sync.ts

    2. Merging at entry level with the user deciding.

      Files src/conflicts.tstests/conflict.test.ts

    3. The remote may hold the only copy of something.

      Files src/sync.ts

    done when · tick each as it passes
    watch out
    • Resolving a sync conflict by taking the most recently written file discards every change made on the other device, and since a vault is edited rarely the loss is discovered months later when a password does not work. Detect divergence, keep both, and merge at entry level with the user choosing.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more security.