feat(app)!: no anonymous accounts on the device (ADR 0020, D4, D25)

Every account has a way in, so the apps make no anonymous account any
more. The start screen shows whenever nobody is signed in, a first
start included, and offers SimpleLogin, a password and GitHub for those
who have it, with no start without an account. An instance out of reach
is said so, with Try again, and one offering no way to create an account
too. An anonymous account's session left from before is deleted as the
app starts (D25), falling back to signing out on the device.

What only anonymous accounts needed goes: signing in anonymously, the
signed-out mark, adding GitHub to an account and switching to a GitHub
account from the Profile tab (with its Cancel, the only one), an
anonymous device's own keys, the warning that signing out deletes the
account, and the texts about keeping progress. Settings says on which
instance the account is signed in. Learn's publishing, reports and
suggestions and its learner count no longer ask whether the person has
an identity: everyone signed in has one.

The server still offers anonymous sign-in until the next commit, so
older apps keep working until v0.20.0 raises the minimum build.

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 14:50:55 +02:00
commit 5b9df73756
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -37,9 +37,14 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
`SubdivisionNames`).
- **Gates** (`lib/src/app/`): `InstanceGate` opens the app's dependencies on
the instance in use (`AppDependencies`, handed down by `AppScope`), anew on
a switch, the previous ones closed first; `SessionGate` keeps a person
signed in, signing in anonymously at a first start and asking how to start
once signed out (`StartScreen`); `PersonScope` opens each person's own
a switch, the previous ones closed first; `SessionGate` shows the app of
whoever is signed in and asks how to start while nobody is
(`StartScreen`: SimpleLogin, a password, GitHub for those who have it,
another instance; an instance out of reach is said so, with Try again,
and one offering no way to create an account too), at a first start as
after signing out; an anonymous account's session, left from before
there were none, is deleted as the app starts (ADR 0020, D25), falling
back to signing out on the device; `PersonScope` opens each person's own
dependencies and closes them when the person goes. `resolveAppLocale`
picks the app's language as every Halkyon app does: the first preferred
language it speaks, never by country alone, British English otherwise.
@ -48,9 +53,9 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
instance runs is (`InstanceSourceCode`, from `AppDependencies.sourceCodeUrl`;
both licences require offering it), the app's own parts (its credits), and
the licence list.
- **The account** (`AccountTiles`): whose account on which instance,
switching to the account on another, signing out (which of an anonymous
account deletes it, so asks first) and deleting it (after asking), for an
- **The account** (`AccountTiles`): on which instance the account is signed
in, switching to the account on another, signing out and deleting it
(after asking), for an
app's settings, in the app's words where only it can say them. While one
runs, the person's app stays and its tile shows a spinner; one that fails
keeps them signed in and says why once, in a snack bar (in a sheet, give
@ -58,26 +63,20 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
the same (Serverpod's `signOutDevice`), so only the device's own storage
fails a sign-out.
- **Signing in** (`lib/src/auth/`): `AuthRepository` on an `AuthGateway`;
`ServerpodAuthGateway` signs in anonymously and with GitHub (the system
browser and a loopback address on Linux and Windows, halkyon_oidc_flutter's
`LoopbackWebAuth`),
`ServerpodAuthGateway` signs in with GitHub those who have it, as it is
added to nobody any more (ADR 0020, D25; the system browser and a loopback
address on Linux and Windows, halkyon_oidc_flutter's `LoopbackWebAuth`),
through the instance's `halkyon_base`, and puts a session granted elsewhere
in place (`useSession`); `SessionBloc`, `GitHubSignInBloc` and
`SignInWaysCubit`; `SignedOutMark`, `SignInAddresses`, `PersonFolders`.
Where the instance offers passwords, GitHub is added to nobody (ADR 0017):
`gitHubOffer` says so from its basics, `GitHubSignInStateReady.linking`
hands it to the screens, which then offer no Add, and the start screen
offers an account to create in place of the app's sentence on adding
GitHub; `gitHubProblemText` says why a GitHub sign-in was refused.
in place (`useSession`); `SessionBloc` and `SignInWaysCubit`;
`SignInAddresses`, `PersonFolders`. The start screen says why a GitHub
sign-in was refused, and for a GitHub account nobody here has, the way to
create an account in its place.
- **Content sealed with data keys** (`lib/src/privacy/`, ADR 0019):
`ContentCipher` (`SodiumContentCipher`, on the isolate it is used on, as an
app seals and opens rows one by one; `FakeContentCipher` in tests) makes a
store's data keys, sealed to the account and signed by it, opens them, and
seals and opens a row's content for the row it is stored in (its table, id
and column), the payload holding the signature before the ciphertext. An
anonymous person's device makes key pairs of its own
(`AccountCrypto.newDeviceKeys`), which `makePassword` keeps as the account's
once they set a password.
and column), the payload holding the signature before the ciphertext.
- **A password and TOTP** (`lib/src/password/`, ADR 0017): `PasswordRepository`
on a `PasswordGateway` (`ServerpodPasswordGateway`), an `AccountCrypto`
(`SodiumAccountCrypto`: halkyon_keys and SRP's client half, each step on an
@ -125,9 +124,8 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
code or opened; and anything a step didn't expect (bytes of the wrong
length from an instance, libsodium's or the keyring's own errors) fails it
rather than leaving its sheet busy. `register_interop_test.dart` holds the
real cryptography to the register's own checks. Anonymous people get no
keys until something is encrypted (step 4). Every build of an app needs a C
compiler and make: sodium's build hook compiles libsodium.
real cryptography to the register's own checks. Every build of an app
needs a C compiler and make: sodium's build hook compiles libsodium.
- **Signing in with SimpleLogin** (`lib/src/simplelogin/`, ADR 0020):
`SimpleLoginRepository` on a `SimpleLoginGateway` (`ServerpodSimpleLoginGateway`,
halkyon_base's `simpleLogin` endpoint) and halkyon_oidc_flutter's
@ -143,18 +141,17 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
keys on the device before any proof is sent; an account opens its keys
with its password; an account of before gets its keys, and its handle
where it has none. With `link`, SimpleLogin joins the account of the
device's person instead, an anonymous person's device keys kept. The
device's person instead. The
screens: the start screen's Continue with SimpleLogin, first where the
instance offers it, and `showSimpleLoginSheet` (`SimpleLoginCubit`),
which waits for the browser with Cancel and stops waiting on the loopback
once it closes. Adding it to an account (ADR 0020, D21, D23): where the
once it closes. Adding it to an account (ADR 0020, D21): where the
person stands (`SimpleLoginRepository.standing`, read anew after every
change, `SimpleLoginStandingCubit`: nothing to show, linked, or
missing), shown by `SimpleLoginSection` (each app places it above
`AccountSecuritySection`: a heading and Add SimpleLogin; once added a row
among the ways in), which reads it once where a
`SimpleLoginStandingScope` is above it. Where the instance offers
SimpleLogin, GitHub is added to nobody (`GitHubOffer.linking`).
`SimpleLoginStandingScope` is above it.
- **Files** (`FileSaver`, `PlatformFileSaver`): keeping a file the app made
where the person chooses (a recovery phrase, recovery codes, an app's
exports): on Linux and macOS the own user's alone (`writePrivately`). On