docs(auth): what building the apps' side settled (ADR 0017)

ADR 0017's step-3 section: codes by mail sign nobody in, the ways in
for accounts without a working password, changes while signed in,
sessions ended in every service, the apps' screens (the QR package and
the alternatives considered), the keys in the keyring, anonymous
people's keys waiting for step 4, and libsodium on devices (results
copied before they leave an isolate; a C compiler and make for every
build). The module's and halkyon_app's READMEs and the AGENTS files say
the same; the snap's build installs make for sodium's build hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-20 19:34:02 +02:00
commit 4f7b123ad4
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -156,12 +156,65 @@ through `AccountRegister`, the session being theirs.
- **Email codes** still sign in (until step 3), but not into an account with a password
that had a confirmed authenticator (`usePassword`), which a code alone would bypass.
Step 3 (email codes go): sign-up keeps `password.set` with its code; `emailCode.verifyCode`
and `emailSignIn.signIn` go, and with them the switch token; accounts with an address but
no password, and accounts whose setup never finished, need a way in (a mailed code and a
new password, as the account reset, or a password plus a mailed code until an
authenticator is confirmed); a member's sessions should end too when the register resets
an account; and a signed-in change of password (with the keys at hand) is still missing.
## Settled in building the apps' side (20 Oct 2026, step 3)
On branch `password-sign-in`, for build 22 (`minimumAppBuild` 22 in Learn; Todo, never
released, stays at 1), after the second reading.
- **Codes by mail sign nobody in** (D4): `emailCode.verifyCode`, `emailSignIn.signIn`
and the switch token went, with the register's operations for them. `emailCode.sendCode`
stays, without a session, as what proves an address: for `password.set`, a sign-up,
a setup never finished and the resets (`accountRecovery.sendCode` went).
- **Ways in for accounts without a working password:** `passwordSignIn.signUp` (a code
and a setup, no session) makes a new account at an address nobody has, or gives the
account with that address and no password (codes by mail before; no keys exist, so
nothing waits) its password, keeping its ID. A setup never finished signs in with its
password and a code mailed to the address the sign-in began with, until an
authenticator is confirmed, and that calls off an account reset waiting (a reset of
the account waiting no longer counts as having had an authenticator, so its owner
isn't locked out). Never for an account that had a confirmed authenticator.
- **Changing while signed in:** the password (`password.beginChange`, `password.change`:
the old password's SRP proof, a TOTP or recovery code, the same keys rewrapped on the
device; every other session ends) and the recovery phrase (`password.setRecoveryKeys`:
the keys wrapped under a new phrase, for a code). A session alone changes neither.
- **Sessions end in every service:** a change of password ends every other session of
the account, a reset of the password or the account all of them, on the register and
on every member, by a notice kept until each confirms (`sessions_ended_notice`, a new
table; hourly with the deletions' notices). A notice names a moment, so sessions made
since stay: a member signing someone in after a reset makes the session once its clock
is past it.
- **A reset not due says when** it takes effect (`takesEffectAt` on the refusal), and the
basics say when GitHub sign-in ends (`gitHubSignInEnds`), which apps show.
- **The apps** (halkyon_app, Learn and Todo): signed out, Sign in and Create an account
(and GitHub, its end date beneath); signing in asks the address and password (Argon2id
on the device, about a second), then a code from the authenticator or a recovery code;
M2 is checked and the keys opened before the session is put in place. An account
never finished is mailed a code; one whose authenticators' reset took effect signs in
without a code; both then set up an authenticator with the session held back, and put
it in place only after. Creating an account: address, password twice, the recovery
phrase shown once (copy, save as a file) and three random words typed back, then the
mailed code (nothing reaches the instance before), the authenticator (a QR code of the
`otpauth://` URI, drawn by the app with pub.dev's `qr`, BSD-3-Clause, pinned; its key
as text; on phones Open in authenticator app; considered: `qr_flutter` and
`pretty_qr_code`, Flutter widgets on the same encoder) and the ten recovery codes
(copy, save). Getting back in: with the phrase (a new password, the keys staying), a
reset of the account asked for by a code (72 hours), and finishing it (a new code; new
keys and a new phrase, or with the phrase the old keys rewrapped, as the account's
reset keeps them for someone who lost both password and authenticator). The account
part (`AccountSecuritySection`): setting up a password (anonymous people, GitHub's, an
account with only an address), finishing the setup, the authenticators (adding one
takes a code from one before), new recovery codes, changing the password, a new
phrase; GitHub's end date.
- **The keys on the device:** unlocked after signing in, kept in the system's keyring
per instance and person (flutter_secure_storage), gone once another person signs in or
nobody, and with the instance's removal from the device. Anonymous people's device keys
wait for step 4: nothing is encrypted before it.
- **libsodium on devices:** sodium's results (salts, public keys, cipher texts,
signatures) are views of native memory its finalizers free once the isolate that made
them ends; each step runs on an isolate of its own, so whatever crosses back is copied
first (a view read freed memory and crashed the process). Every build of an app needs a
C compiler and make for sodium's build hook (Linux, Android's NDK, Xcode, Visual
Studio; the snap's build and CI's runner too).
## Consequences