| Filename | Latest commit message | Latest commit date |
|---|---|---|
ADR 0017, step 3: halkyon_app's side, which Learn and Todo take. - The account's cryptography on the device (`SodiumAccountCrypto`): halkyon_keys and SRP's client half, each step on an isolate of its own. sodium hands out results as views of native memory its finalizers free once the isolate that made them ends, so everything crossing back is copied first: a view read freed memory and crashed the process. - `PasswordGateway` on the module's endpoints (steps without a session through a client holding none; an authenticator set up with a session granted but not yet the device's), and `PasswordRepository` running the flows: signing in (M2 checked, the keys opened before anything is trusted), signing up, a password for someone signed in, an account never finished, the authenticators, recovery codes, changing the password, a new recovery phrase, and getting back in. - The unlocked keys in the system's keyring per instance and person (`SecureAccountKeyStore`), gone once another person signs in or nobody, and with the instance's removal. - The screens: the start screen's Sign in and Create an account (and when GitHub sign-in ends), the sign-in sheet (codes, recovery codes, an account never finished, a lost authenticator), the account's setup (the recovery phrase shown once, three random words typed back before anything reaches the instance; the authenticator's QR code, drawn with pub.dev's qr; the recovery codes), getting back in, and the account section (`AccountSecuritySection`). - `FileSaver` moves from Learn into halkyon_app, for the phrase and the codes in Todo too; `AppDependencies` names it and `passwords`. - halkyon_keys rebuilds account keys from the secret keys a device kept; the key proof's message is in halkyon_base_client, checked against the register's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me> |
||
| .. | ||
| assets | ||
| lib | ||
| test | ||
| tool | ||
| .gitignore | ||
| analysis_options.yaml | ||
| CHANGELOG.md | ||
| l10n.yaml | ||
| LICENSE | ||
| pubspec.yaml | ||
| README.md | ||
halkyon_app
What every Halkyon app (Learn, Todo, Train) is built around, on an instance's
halkyon_base module: the instances a device keeps an account on, signing in,
each person's data opened and closed, and syncing it. Taken from Halkyon Learn,
which it was written for, so it behaves as Learn's own did; the texts are in
British English, American English and German, and where they name the app,
they name yours.
Written as the apps are: Dart 3.13's primary constructors, Material from
package:material_ui, Lucide icons, blocs (flutter_bloc), LeanCode's lints.
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 session key),InstanceChecker(does an address answer, where does it keep people's data;ServerpodInstanceCheckerasks itsinstance.basics()),InstanceScope. The screens:FirstStartScreen(the build's instance and another, as tiles,InstanceTile),InstanceStep(an address, checked),showAccountSwitcherandAddAccountScreen, and where an instance keeps people's data, as a flag and words (InstanceLocationBuilder,InstanceFlag,instanceLocationWords):EU, a country or a subdivision (ISO 3166-2,US-CA) as its operator says, "Germany (EU)" for a member of the European Union (europeanUnionMembers) and "California, United States" for a subdivision, with the most specific flag there is (a few subdivisions have their own, such as Scotland; else the country's). Countries and subdivisions are named by Unicode CLDR's names in English and German, which come with the package (AssetCountryNames,AssetSubdivisionNames; the package'stool/country_names.pyandtool/subdivision_names.pywrite them, from one release); tests give names of their own (CountryNames,SubdivisionNames). - Gates (
lib/src/app/):InstanceGateopens the app's dependencies on the instance in use (AppDependencies, handed down byAppScope), anew on a switch, the previous ones closed first;SessionGatekeeps a person signed in, signing in anonymously at a first start and asking how to start once signed out (StartScreen);PersonScopeopens each person's own dependencies and closes them when the person goes.resolveAppLocalepicks the app's language as every Halkyon app does: the first preferred language it speaks, never by country alone, British English otherwise. - About (
showAppAbout): the app's name (its frame's), its version, icon and legalese where it gives them, where the source of what the instance runs is (InstanceSourceCode, fromAppDependencies.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 app's settings, in the app's words where only it can say them. - Signing in (
lib/src/auth/):AuthRepositoryon anAuthGateway;ServerpodAuthGatewaysigns in anonymously, with GitHub (the system browser and a loopback address on Linux and Windows,LoopbackWebAuth) and with a code by mail, through the instance'shalkyon_base;SessionBloc,GitHubSignInBloc,EmailSignInBloc(withshowEmailSignInSheet) andSignInWaysCubit;SignedOutMark,SignInAddresses,PersonFolders. - 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),AppCompatibilityandcompatibleSync(the instance's minimum app build, the engine's table check),SyncStatusBloc,syncAndWait, and the widgets:SyncStatusCard,AppMismatchBanner,SyncTroubleBadge. - A client of
halkyon_basealone (BaseClient): to ask an instance about itself, and to sign in as another person than the app's client holds. - Testing (
package:halkyon_app/testing.dart): fakes of every boundary (FakeAuthGateway,FakeInstanceChecker, the stores in memory), a frame (testFrame),testApp, andTestAppDependencies.
Using it in an app
-
The server includes
halkyon_basewith the nicknamebase(its README), so the app's generated client hasclient.modules.base. -
Depend on it by path, within the repository's workspace, as on
halkyon_ui:dependencies: halkyon_app: path: ../../../packages/halkyon_app -
Texts: add
HalkyonAppLocalizations.delegateto everyMaterialApp's delegates, besideHalkyonUiLocalizations.delegateand material_ui'sGlobalMaterialLocalizations.delegates. -
The frame (
AppFrame): the app's name and wordmark word, its icon, an example address, its theme, delegates, languages (resolveAppLocaleas 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. -
The dependencies: the app's composition root implements
AppDependencies<P>(serverName,auth,signedOut,sourceCodeUrl,openPerson,dispose), its person's dependenciesDisposable. Itsauthis anAuthRepositoryonServerpodAuthGateway(client, base: client.modules.base, callbackScheme: …, appName: …); the scheme is the 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 ishttp://127.0.0.1/github-callback(port 43823 unless given). -
The tree:
InstanceGate( frame: frame, choice: choice, checker: ServerpodInstanceChecker(app: 'todo'), readLook: …, // the device's theme mode and language open: Dependencies.create, app: SessionGate( look: (context) => …, app: (person) => PersonApp(person: person), // with PersonScope inside ), )A person's widgets find their dependencies with
PersonScope.of<PersonDependencies>(context)and the app's withAppScope.of<Dependencies>(context). -
Licences: call
registerHalkyonAppLicenses()once at startup (the flags' and Unicode CLDR's notices), beside halkyon_ui'sregisterFontLicenses().
Licence
GNU Affero General Public License 3.0 or later (LICENSE). The flags are
lipis' flag-icons, drawn by the flag package, under the MIT licence
(assets/flags/LICENSE-flag-icons.txt). The subdivisions' names are Unicode
CLDR's (release 48.2), under the Unicode License v3
(assets/subdivisions/LICENSE-CLDR.txt, assets/subdivisions/ATTRIBUTION.md).