Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "writui"
version = "0.19.0"
version = "0.20.0"
edition = "2024"
description = "A terminal writing app. No nonsense. Markdown. Local. Encrypted."
license = "MIT"
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ fingerprint to the Mac turns it off until you next unlock with the
password. Your vault's key, sealed so that only this Mac's Secure Enclave
can open it after a fingerprint, is kept in `~/.config/writui/touch-id/`.

To change the password, open Settings and choose "Change password": type
your current password, then the new one twice. writui re-encrypts the
vault with the new password, keeping a backup of it until the vault has
reopened with the new password and passed its checks; if anything goes
wrong, the old password keeps working. Touch ID is set up again for the
new password on its own.

- **Posts** are listed with the most recently changed first. Open one with
Enter or a click, start a new one with `n` (or Enter: "New" is
highlighted when the list opens). The list's shortcuts are single keys,
Expand Down
36 changes: 36 additions & 0 deletions docs/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -785,3 +785,39 @@ copied in the meantime. The trade-off: "select, Cmd+C, then type over the
selection" loses the copy (Cmd+X is the way to do that). Non-text clipboard
contents (an image) can't be remembered, so selecting text replaces them.
Tests use a per-thread fake clipboard rather than the real one.

## Changing the password: rekey in place, checked end to end

`Vault::change_password` checks the current password (Argon2 with the
file's salt must give the open vault's key), makes a new random salt and
the new password's key, takes a fingerprint (SHA-256 over the schema
version and every row of every table), backs the file up next to itself
(`writui.db.password-<time>.bak`), then runs `PRAGMA rekey` with the raw
key and new salt. SQLCipher rewrites every page in one transaction and
puts the new salt in the file's first 16 bytes. The change counts only
once the vault opens from scratch just as an unlock would (salt read from
the file, key from the new password), passes `cipher_integrity_check` and
`integrity_check`, and has the same fingerprint; then the backup is
deleted. (If deleting it fails, Settings names it: the old password opens
it.)

If anything fails after the backup, the old connection is closed and the
vault reopened with the old key, checking the salt, integrity and
fingerprint; if that doesn't pass (the rekey went through, or left a
damaged file), any `-journal` is removed (it belongs to the failed
change, not the backup) and the backup copied back over the vault, then
checked the same way. Only if that fails too is the vault left closed:
writui stops with an error naming the backup. Tests break the change
after the rekey (a failing check, a damaged vault, a damaged vault and
backup) through a hook, and check the old password still works each time.

Rekeying in place was the creator's choice over `sqlcipher_export` into a
new file and renaming it. A second writui open on the same vault would
lose access when the key changes under it; nothing stops two from running
today, and this doesn't make that worse in a way worth a lock file yet.

Touch ID holds the vault's key, so after a change that went through, its
file is made again with the new key (sealing needs no fingerprint); if
that fails it's turned off, and Settings says so. The dialog (three boxed
password fields, stacked) is its own small screen, `tui::change_password`,
since the general dialog only has one plain text field.
4 changes: 3 additions & 1 deletion docs/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ the logo, adds an Unlock button, trims the setup screen, and makes Esc on
the list quit without asking. `0.18.0` unlocks with Touch ID on Macs
(asked for out of order), and `0.19.0` adds the Markdown styling setting,
list continuation and Cmd+C (also out of order; it replaces the closed
PR #31's bold-title switch). Next:
PR #31's bold-title switch). `0.20.0` adds Change password in Settings
(out of order too). Next:

1. **Try online** (writui.com/try), below: the creator wants it next, and
it must work on a phone. Probably two or three PRs, in the order listed.
Expand Down Expand Up @@ -141,6 +142,7 @@ writui in the browser at writui.com/try, phones included (see the spec's
## M6 — LLM chat sidebar

- [x] Settings screen, stored in the vault: author (for front matter), line width
- [x] Change password (in Settings), with Touch ID set up again
- [ ] More settings: what Tab types, and the rest listed in the spec
- [ ] API keys for OpenAI + Anthropic in settings (or prompt on first open)
- [ ] Sidebar that fits next to the editor (or takes over on narrow terminals)
Expand Down
32 changes: 31 additions & 1 deletion docs/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,37 @@ in [DECISIONS.md](DECISIONS.md); the build order lives in [PLAN.md](PLAN.md).
added, which deliberately voids it), writui says so, and the next
password unlock sets it up again. Where it's available but off, the
unlock screen's Overview gets one more line pointing to Settings.
- Change password — planned (it must set Touch ID up again).

> Add a "Change password" setting. On the Settings screen it's a row
> like the others (with a faded line under it, e.g. "Asks for your
> current password, then a new one twice."). Enter or a click opens a
> dialog with three boxed password fields, like the setup screen's:
> current password, new password, confirm new password. Tab moves
> between them, Enter submits, Esc cancels. A wrong current password or
> a confirmation that doesn't match says so in the dialog and keeps the
> dialog open. Show a short "Changing the password…" while it works
> (Argon2 is slow), then "Password changed." on the Settings screen.
>
> This touches my real writing, so be careful. Re-encrypt the vault in
> place with SQLCipher's rekey: a new random salt and the new Argon2
> key. Back up the vault file first, and delete the backup only after
> the vault reopens with the new password and passes its integrity
> check. If anything fails, the old password must still work and
> nothing may be lost. If Touch ID is on, redo its key file for the new
> key (DECISIONS says this is needed), or turn Touch ID off and say so.

- **Change password**: the last row of Settings. Its dialog has three
boxed fields (current, new, confirm new) under a reminder that there's
no recovery. Tab / ↑↓ move between the fields; Enter goes on to the
next empty one, then changes the password; Esc or "Cancel" closes it;
fields and buttons can be clicked. The new password needs at least 8
characters, like the first one. Mistakes (wrong current password, the
confirmation not matching, too short) are said in red in the dialog,
which stays open. While it works the dialog says "Changing the
password…"; then Settings says "Password changed." If Touch ID is on,
it's set up again for the new password; if that fails, Touch ID is
turned off and Settings says so. If the change fails, Settings says why
and that the old password still works.

## The screens

Expand Down
Loading
Loading