docs(auth): the device side's rest of the full reading (ADR 0017)

ADR 0017's full-reading part says what the device's fixes did: the
clipboard, plain HTTP refused but on this machine, sheets never left busy,
what Save leaves, secrets out of debug logs, keyboards, screenshots and
backups, memory, and the real cryptography against the register's checks.
halkyon_app's README says what an app must add (its activity's screen
guard), Learn's AGENTS.md the HTTPS rule for instances.

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 15:12:43 +02:00
commit a72b96d628
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -13,8 +13,12 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
## What it holds
- **Instances** (`lib/src/instance/`): `Instance` (an address as people type
it), `InstanceChoice` (those the device keeps an account on, one in use;
`FileInstanceStore`), `InstanceStorage` (each instance's people's folders and
it; `isSecure`: over HTTPS, or over plain HTTP on this machine alone),
`InstanceChoice` (those the device keeps an account on, one in use;
`FileInstanceStore`; `allows` an instance reached securely, or in a
development build the build's own over HTTP, and `leave`s one it no longer
allows, which `InstanceGate` never opens: it says why and offers another or
removing it), `InstanceStorage` (each instance's people's folders and
session key), `InstanceChecker` (does an address answer, where does it keep
people's data; `ServerpodInstanceChecker` asks its `instance.basics()`),
`InstanceScope`. The screens: `FirstStartScreen` (the build's instance and
@ -61,7 +65,8 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
sodium's are views of memory freed with the isolate that made them) and an
`AccountKeyStore` (`SecureAccountKeyStore`: the account's unlocked keys in
the system's keyring, per instance and person, gone once another person
signs in or nobody). Signing in (SRP's M2 checked and the keys opened before
signs in or nobody; on iOS and macOS `first_unlock_this_device`, so never
in a backup restored elsewhere nor in iCloud Keychain). Signing in (SRP's M2 checked and the keys opened before
anything is trusted), signing up (the recovery phrase shown once and three of
its words typed back before anything reaches the instance), a setup never
finished, the authenticators (QR codes drawn with pub.dev's `qr`), recovery
@ -76,12 +81,27 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
digest of what it sets, `proof_digests.dart`), so nobody between the device
and the instance swaps anything in; deleting by a code by mail
follows `secondFactorWorks`, as the instance tells it, and asks for the
address only where the device doesn't know it. 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.
address only where the device doesn't know it. Secrets shown stay the
person's: the recovery phrase is saved or written down, never copied;
recovery codes and an authenticator's key are copied and the clipboard
emptied a minute later (`copySecret`); all of them show in a
`SecretScreen`, kept out of screenshots and recents on Android
(`FLAG_SECURE`: the app's `MainActivity` answers `app.halkyon/screen_guard`,
as Learn's and Todo's `ScreenGuard.kt` do) and covered on iOS while the app
isn't in front; keyboards learn nothing typed into a password, a code or
the phrase; no state's text (which a debug build's bloc observer logs) holds
a secret, a code or the address; only an `otpauth:` URI is shown as a QR
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.
- **Files** (`FileSaver`, `PlatformFileSaver`): keeping a file the app made
where the person chooses (a recovery phrase, recovery codes, an app's
exports).
exports): on the desktop the own user's alone (0600, `writePrivately`), on
iOS without the copy file_picker leaves in the temporary folder
(`forgetPickerCopy`).
- **Sync** (`lib/src/sync/`): `SyncService` (once at start, shortly after
local changes, on resume and every few minutes, one sync at a time, counting
the changes not on the server yet, `PendingChangesStore`), `AppCompatibility`
@ -116,7 +136,10 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
its resolution) and launch screen, and the three sentences only it can
say: what the first start's instance keeps of the person's, what a switch
to another account leaves behind, and where GitHub is added later.
5. **The dependencies**: the app's composition root implements
5. **Android**: the app's `MainActivity` answers `app.halkyon/screen_guard`
(copy Learn's or Todo's `ScreenGuard.kt` and call `ScreenGuard.attach` in
`configureFlutterEngine`), or secrets show in screenshots and recents.
6. **The dependencies**: the app's composition root implements
`AppDependencies<P>` (`serverName`, `auth`, `signedOut`, `passwords`,
`fileSaver`, `sourceCodeUrl`, `openPerson`, `dispose`), its person's
dependencies `Disposable`. Its `passwords` is a `PasswordRepository` on
@ -128,7 +151,7 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
app's own (`app.halkyon.learn`), registered with each platform and in the
instance's GitHub OAuth app, whose loopback address on Linux and Windows is
`http://127.0.0.1/github-callback` (port 43823 unless given).
6. **The tree**:
7. **The tree**:
```dart
InstanceGate(
@ -147,7 +170,7 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
A person's widgets find their dependencies with
`PersonScope.of<PersonDependencies>(context)` and the app's with
`AppScope.of<Dependencies>(context)`.
7. **Licences**: call `registerHalkyonAppLicenses()` once at startup (the
8. **Licences**: call `registerHalkyonAppLicenses()` once at startup (the
flags' and Unicode CLDR's notices), beside halkyon_ui's
`registerFontLicenses()`.