feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)

Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).

- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
  says whether a sign-up could take one, only while that sign-up's
  mailed code waits (`codeExpired` otherwise), counted per code request
  (30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
  (D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
  the code for another try. An existing account at the address keeps
  its own handle where it has one, and the typed handle is still
  checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
  share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
  300 ms after typing pauses, only about the handle typed last; the
  instance's suggestion is free without asking) (D30). The password
  setup asks for the handle between the phrase check and the code, and
  a refusal at the code sends back to it. SimpleLogin's sign-up and
  keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.

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-24 18:09:04 +02:00
commit 630965d920
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -1,6 +1,6 @@
# ADR 0020: Signing in with SimpleLogin only
- **Status:** decided 23 Oct 2026, by the developer (D1–D20 below; D21–D28 on 24
- **Status:** decided 23 Oct 2026, by the developer (D1–D20 below; D21–D30 on 24
Oct), the *Proposal* with it (D15), as D18 to D20 change it and D24 drops the
cut-off's machinery (D8's code, D10, D22, 10's `ANONYMOUS_SESSIONS_END`, 11, step 5).
- **Built (23 Oct 2026), step 1:** the forwarding page (3),
@ -91,6 +91,19 @@
(`FILE_LIMIT_ANONYMOUS_MB` went), and `minimumAppBuild` is 23 (v0.20.0), as builds up
to 22 start anonymously. Learn's sync tests make their people and sessions by SQL,
as Serverpod's server-side sessions make them, in place of an anonymous sign-in.
Then the handle at password sign-up (D26, D28–D30): `passwordSignIn.signUp` takes
one and `password.set` takes one from an account without (`SignInWays.handle` tells
the app), each checked before the code is used, so a refusal (`ProfileRefusedException`
as the Profile's check gives it) leaves the code for another try; a new account's
profile is private (D5). `passwordSignIn.checkHandle` answers while the code travels,
only for a sign-up's code request still waiting (`codeExpired` otherwise), counted per
request (30 an hour) and network (60), as the register's members ask the register.
An existing account given the address's code keeps its own handle where it has one,
the typed one still checked, so the answer tells nothing of the address. In the apps,
one `HandleField` with its `HandleCheckCubit` (asked 300 ms after typing pauses, only
for the handle typed last, the instance's suggestion free without asking) serves the
password setup's handle step, between the phrase check and the code, and
SimpleLogin's sign-up and keys steps.
- **Builds on:** ADR 0010 (email sign-in), ADR 0011 (profile pages), ADR 0016 (the
register), ADR 0017 (a password, TOTP and the keys).
- **Changes:** ADR 0017's way in on an instance that offers SimpleLogin (its keys, their
@ -271,6 +284,15 @@ against app.simplelogin.io on 23 Oct:
- **D28 No prompt for an account without a handle** (24 Oct, night, recommended, over a
step after signing in): the developer sets theirs in Profile; a handle is asked where
one is required and the account has none (a password added, the keys set).
- **D29 The handle is chosen while the code travels** (24 Oct, recommended, over a step
before the address, which would let anyone ask about handles without mailing a code,
and over one after the code, which would use the code up on a taken handle): the step
comes after the phrase check has mailed the code and before it is typed, and its free
check is tied to that code request.
- **D30 One handle field everywhere** (24 Oct, recommended, over keeping SimpleLogin's
own): the same field, checking as it is typed, for password sign-up, setting a
password, SimpleLogin's sign-up and its keys step; the handle the instance suggested
counts as free without asking.
- **Also (23 Oct):** the developer's own account moves by adding SimpleLogin to it while
signed in with GitHub; the instance's SimpleLogin app goes up for approval once
halkyon.app/learn exists, before learn.halkyon.eu switches.