docs(auth): the full reading, and the rules as they stand now (ADR 0017)

ADR 0017's "Full reading (21 Oct 2026)": each server finding and what was
done (H1, M1, M2, M3, L1, L4, L5, L6, L7, L10), those accepted with their
reasons (L2, L3, L8, L9), and the device's M1, the owner's proof bound to
its operation and the whole base URL, still to do. "The rules as they
stand" brought up to date; the module README and Learn's AGENTS.md too.

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-21 13:39:18 +02:00
commit 8ec2743bfa
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -263,8 +263,9 @@ Built on the three parts above, on the register (members forward every step thro
- **Codes tried in a row** (`second_factor_waits.dart`, table
`second_factor_failures`): every code tried against an account's second factor after
a right password or phrase at sign-in or in the phrase's reset (never the owner's
proofs while signed in) counts, per account, from every network, before it is checked, one at a time
per account (an advisory lock), so codes sent at once get no more checked; every tenth
proofs while signed in) counts, per account and way it came (`SecondFactorPath`:
after the password, after the phrase, each with its own waits), from every network,
before it is checked, one at a time per account (an advisory lock), so codes sent at once get no more checked; every tenth
in a row begins a wait as it is counted (`codesWait`, with when), an hour, then twice
as long each time, a day at the most, and every wait mails the account's address
(which those calls take); a code accepted, either reset carried out, and a new
@ -280,7 +281,12 @@ Built on the three parts above, on the register (members forward every step thro
account's signing key, which every signed-in device keeps) and a code set a new
password, the keys staying. A lost password without it: a mailed code asks, 72 hours
pass (a mail at the start, a sign-in calls it off), a new code carries it out: new
password, new keys, no authenticators.
password, new keys, no authenticators. While the owner is held out of signing in (codes
waiting, the address's ceiling of proofs filled), every reset not carried out is pushed
to 72 hours after the hold-out ends (`pushOutResets`, `account_resets.dart`); a reset
that took effect and wasn't carried out lapses a week later (`resetLapsesAfter`,
pruned by `forgetLapsedResets`); `SignInWays` tells signed-in devices when each waiting
reset takes effect, and `password.callOffResets` calls them off for the owner's proof.
- **Changing while signed in:** `password.change` takes the owner's proof (the old
password's SRP proof for a `password.beginChange` challenge, and a TOTP or recovery
code) and the same keys wrapped under the new password; `password.setRecoveryKeys`
@ -297,22 +303,26 @@ Built on the three parts above, on the register (members forward every step thro
(`request`, `code`), to the account's own address. An account without a password
deletes with its session, as before.
- **Sessions end everywhere:** a change of password ends every other session of the
account; a reset of the password or the account ends all. The register ends its own
and keeps a `sessions_ended_notice` per member (told at once and hourly until it
confirms, `deliverSessionNotices`, as deletions are told): it names a moment, so a
account (a setup's password replaced too); a reset of the password or the account ends
all. The register ends its own and keeps a `sessions_ended_notice` per member (told at
once in the background, and hourly until it confirms, `deliverSessionNotices`, as deletions are told): it names a moment, so a
session made since stays, and the session that asked for a change. A member answering
one (`/halkyon-base/member/sessions-ended`) ends its sessions made before the moment;
a member signing someone in after a reset ends its own first and makes the new session
only once its clock is past the moment (`issueSessionAfter`), refusing with
`passwordChanged` (sign in again with the new password) when its clock is more than
`maxClockSkew` behind. A refusal for a reset not due says when it takes effect
`maxClockSkew` behind or ahead. The owner's warning of a session ended for its proofs is
dismissed only by a session made since (`signedInBefore`). A refusal for a reset not due says when it takes effect
(`takesEffectAt`).
- **Older ways:** an account that signs in with a password (it had a confirmed
authenticator, which a reset may have taken since: `passwordOnly` in its ways in) no
longer signs in with GitHub (`usePassword`); where passwords are offered, no new GitHub
account is linked (`deprecated`); from `GITHUB_SIGN_IN_ENDS` on GitHub signs nobody in
(`ended`), the basics stop naming the day (`gitHubSignInEnds`, which apps show until
then) and `forgetGitHubAccounts` deletes every GitHub ID.
then) and `forgetGitHubAccounts` deletes every GitHub ID, first deleting the accounts
that sign in with GitHub alone (no address, never a password). `password.set` adds an
address without GitHub only for someone anonymous where the session lives
(`anonymousHere`, which a member tells the register).
## Taking it into a host