halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
/// What every Halkyon app is built around, on an instance's halkyon_base:
|
|
|
|
|
///
|
|
|
|
|
/// - **Instances**: the ones a device keeps an account on, one in use at a
|
|
|
|
|
/// time (`InstanceChoice`), chosen at first start (`FirstStartScreen`) or
|
|
|
|
|
/// added later (`showAccountSwitcher`), each checked before the app moves
|
|
|
|
|
/// there (`InstanceChecker`) and saying where it keeps people's data.
|
|
|
|
|
/// - **Gates**: `InstanceGate` opens the app's dependencies on the instance
|
|
|
|
|
/// in use (`AppDependencies`, `AppScope`); `SessionGate` keeps a person
|
|
|
|
|
/// signed in and asks how to start once signed out (`StartScreen`);
|
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
|
|
|
/// `PersonScope` opens each person's own dependencies. While an account
|
|
|
|
|
/// waits for the instance's operator, `WaitScreen` shows where its wait
|
|
|
|
|
/// stands (Halkyon Learn's ADR 0021, D31).
|
feat(app,learn): the device's appearance and language, halkyon_app's, as Learn's Settings show them
The developer asked for Todo, Train and Meditate to offer Learn's
Appearance and Language (ADR 0014's amendment of 4 Nov 2026). Their
model, store, cubit and card move into halkyon_app: Appearance and Look
(a BCP 47 tag, null for the device's language), FileLookStore keeping it
in the app's support folder as JSON written by rename (per device, not
synced, read before the first frame), LookCubit for the apps to provide
above InstanceGate, and LookSettingsCard / DeviceLookCard with the texts
moved from Learn's arbs (look*, the same words). Each app passes the
languages it speaks, named in their own language.
Learn keeps its stored choice where it was, among its DeviceSettings,
and feeds the shared card from them: no migration, so nobody's language
or appearance moves, and nothing changes on its screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:30:29 +01:00
|
|
|
/// - **The look**: the device's appearance and language (`Look`), kept on
|
|
|
|
|
/// it (`FileLookStore`) and applied before anyone signs in (`LookCubit`),
|
|
|
|
|
/// chosen in Settings (`DeviceLookCard`, `LookSettingsCard`; ADR 0014's
|
|
|
|
|
/// amendment of 4 Nov 2026).
|
refactor(halkyon_app): the account section, About, the language and countries' names, shared
What Learn and Todo each held now comes from halkyon_app:
- AccountTiles: whose account on which instance, switching, signing
out (asking first for an anonymous account, as it deletes it) and
deleting it (asking first), the app's own words passed in.
- showAppAbout: the app's name, version, icon and legalese as it gives
them, the source of what the instance runs (AppDependencies gains
sourceCodeUrl), the app's own parts, the licence list.
- resolveAppLocale: the first preferred language the app speaks, never
by country alone, British English otherwise.
- AssetCountryNames, the default of InstanceGate and InstanceScope:
Unicode CLDR 48.2's territory names in English and German
(tool/country_names.py, 7.6 KB), Hong Kong, Macao, Myanmar and
Palestine by their short names as Learn's world map has them, so
Todo says "Germany (EU)" where it said "DE (EU)".
Learn's texts read as before in every language; its About bloc and
gateway, its country names from the world map and the keys only the
moved code used are gone, as are Todo's copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 23:51:52 +02:00
|
|
|
/// - **The account and About**: `AccountTiles` for an app's settings,
|
|
|
|
|
/// `showAppAbout` with the source of what the instance runs, and
|
|
|
|
|
/// `resolveAppLocale`, the language every Halkyon app picks.
|
feat(app,learn,todo): the operator's "This instance" card, screen and badge (ADR 0021, step 1)
Whoever operates the instance approves, refuses and suspends accounts
and names other operators in the app (D21 to D24, D34):
- halkyon_app: InstanceOperatorRepository on
ServerpodInstanceOperatorGateway (the module's instanceOperator
endpoint; refusals as OperatorFailed: notFound, isOperator,
lastOperator, notAllowed for 401 and 403, failed), in AppDependencies.
OperatingCubit counts the accounts that wait: asked as the person's
app opens, after every change, and every 5 minutes only while they
operate the instance, so nobody else's app keeps asking.
InstanceOperatorCard (or its tiles) and WaitingAccountsBadge show
nothing at all for anyone else. InstanceOperatorScreen, laid out as
Settings: the accounts that wait, each showing its four words to read
out before Approve (D33), Refuse for new ones after asking; who
operates, Take back after asking, the last always kept; Name an
operator by handle; Find an account by the start of its handle, with
Suspend after asking and Name as operator. English and German texts
("Admin" as the role word in German).
- Learn: the card right after the Sync card in Settings, which the
cogs' count leads to as their dot leads to Sync; the count on the
Settings cogs of Home and Profile.
- Todo: the tiles in the account sheet and the count on its button.
Todo's server is a member, whose `operating()` is always null, so
nothing shows there until it holds the register.
Tests: the gateway's mapping, the repository, both cubits with their
denials, the card, the badge and the screen; Learn's Settings shows the
card and the count to an operator and neither to anyone else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:55:28 +01:00
|
|
|
/// - **The instance's operators**: `InstanceOperatorCard` (or
|
|
|
|
|
/// `InstanceOperatorTiles`) and `WaitingAccountsBadge` for whoever
|
|
|
|
|
/// operates the instance, on `OperatingCubit`, leading to
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
/// `InstanceOperatorScreen`, an overview whose screens approve, refuse and
|
|
|
|
|
/// suspend accounts and name operators (Halkyon Learn's ADR 0021, D21 to
|
|
|
|
|
/// D24, D61, D62).
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
/// - **Assistants**: `AssistantsCard` for an app's settings, what the
|
|
|
|
|
/// operator grants a person and their own keys where allowed, on
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
/// `AssistantRepository`; `AssistantGrantsScreen` from the operator's
|
|
|
|
|
/// screen, on `AssistantOperatorRepository` (Halkyon Learn's ADR 0021,
|
|
|
|
|
/// step 2, D57 to D63).
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
/// `package:halkyon_app/assistant_models.dart` holds the models alone.
|
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>
2026-10-24 14:50:55 +02:00
|
|
|
/// - **Signing in**: with SimpleLogin (ADR 0020: `SimpleLoginRepository`,
|
|
|
|
|
/// its sheet and `SimpleLoginSection`), with a password and TOTP (ADR
|
|
|
|
|
/// 0017: `PasswordRepository`, its sheets and `AccountSecuritySection`),
|
feat(base,app)!: GitHub signs nobody in any more (ADR 0020, D36)
Every account signs in with SimpleLogin or a password now, so GitHub
goes from the module, the shared app package, Learn and Todo, ahead of
ADR 0017's GITHUB_SIGN_IN_ENDS.
- halkyon_base: no `gitHubSignIn` endpoint, GitHub identity provider or
GitHub refusals; no `githubClientId`/`githubClientSecret`, no
`GITHUB_SIGN_IN_ENDS` and no `forgetGitHubAccounts`; the register
loses `account-with-github`. `SignInWays.gitHub`, the basics'
`gitHubClientId` and `gitHubSignInEnds`, and `PasswordRefusal`'s
`signInAgain` and `noAddress` go. A first password takes a code
mailed to the person's own address; an account without one sets none
(`wrongCredentials`).
- Learn's server: `instance.info()` without `gitHubClientId`, the
pruning without GitHub, and `minimumAppBuild` 24, as builds up to 23
(v0.20.0) require `SignInWays.gitHub`: the server deploys with the
release of build 24.
- halkyon_app: no GitHub button or problems on the start screen, no
password offered after a GitHub sign-in, and `ServerpodAuthGateway`
without the browser half only GitHub used (`(client, base:)`). The
account section offers a first password only to an account with an
address. `LoopbackWebAuth` and the apps' schemes stay, SimpleLogin's
ways back.
- Learn's Profile lists no GitHub; the texts go (English, German).
- Serverpod's `serverpod_auth_idp_github_account` stays, as its
migrations make it; a row goes with its account, and ADR 0020's D36
gives operators the SQL to delete those left.
- deploy/: compose.yaml and .env.example without GitHub. The READMEs,
AGENTS.md files and ADR 0017 amended.
BREAKING CHANGE: builds up to 23 can't read the instance's answers
about an account's ways in; minimumAppBuild is 24.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 12:52:52 +01:00
|
|
|
/// the session on `AuthRepository` and `ServerpodAuthGateway`, the
|
|
|
|
|
/// account's keys on the device. Every account has a way in: there are no
|
|
|
|
|
/// anonymous ones (ADR 0020, D4).
|
feat(app): what's new after an update, drafted by the release tool (ADR 0030, D61)
halkyon_app's WhatsNew reads what each release changed from the app's
assets/whats_new/ (<version>.<language>.txt, English and German, a change
a line): after an update a sheet lists every release since the one the
device showed last, newest first, once; Settings shows the running
version's again, above About. Nothing on a fresh install.
tool/release.dart drafts the missing texts from the commits that count
after "Go on?" (German marked TRANSLATE:), opens them in the editor until
they fit (no mark left, F-Droid's 500 characters) and commits them with
the version. The English is the Forgejo notes' What's new, each language
F-Droid's changelog of the build, written at publishing and copied in by
tool/fdroid_repo.sh. Learn and Train bundle their texts, Learn's 0.22.0
and Train's 0.1.0 written to start.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 19:36:20 +01:00
|
|
|
/// - **What's new**: `WhatsNew` reads what each release changed from the
|
|
|
|
|
/// app's `assets/whats_new/` (`tool/release.dart` drafts it), and
|
|
|
|
|
/// `WhatsNewOnUpdate` shows it once after an update, `WhatsNewTile` in
|
|
|
|
|
/// Settings again, both under the app's `WhatsNewScope`.
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
/// - **Sync**: `SyncService` on triggers, the changes still waiting, and
|
|
|
|
|
/// where it stands (`SyncStatusBloc`, `SyncStatusCard`, `AppMismatchBanner`,
|
|
|
|
|
/// `SyncTroubleBadge`).
|
feat(base,app,keys): keys of shared spaces, sealed to each member
Step 3 of ADR 0044 (D3, D18, D19). A shared space's data keys are
sealed on a device to each member's public keys; the instance keeps
what devices sealed and never opens it.
- halkyon_keys: the safety code of two accounts' public keys
(`safetyCode`), 60 digits, the same on both sides.
- halkyon_base: the register hands out accounts' public keys
(`public-keys-of`, local and remote); the table `shared_space_key`
and the endpoint `sharedSpaceKeys` (memberKeys, keysNeeded, seal,
mine). Only the Owner and Editors seal, the first key sealed to a
member stays, and a member's keys go with their membership. Every
host's migration creates the table.
- halkyon_app: `ContentCipher` seals a key to another account and
opens one sealed by another; `SharedKeys` shares a space's key with
its members, seals keys to newcomers and opens the person's own,
holding each member to the public keys `KeyPins` pinned on first
sight (`key_pins.json` in the person's folder), a change refused
until trusted anew.
Learn takes them into its `Privacy` in step 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 11:28:13 +01:00
|
|
|
/// - **Shared spaces' keys** (Halkyon's ADR 0044, D3, D18): `SharedKeys`
|
|
|
|
|
/// seals a space's data keys to its members and opens those sealed to
|
|
|
|
|
/// the person, holding each member to the public keys `KeyPins` pinned
|
|
|
|
|
/// on first sight, with the safety code to compare them by.
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
///
|
|
|
|
|
/// An app gives its name, look and own words as an `AppFrame`, and installs
|
|
|
|
|
/// `HalkyonAppLocalizations.delegate` beside its own.
|
|
|
|
|
library;
|
|
|
|
|
|
feat(base,app,keys): keys of shared spaces, sealed to each member
Step 3 of ADR 0044 (D3, D18, D19). A shared space's data keys are
sealed on a device to each member's public keys; the instance keeps
what devices sealed and never opens it.
- halkyon_keys: the safety code of two accounts' public keys
(`safetyCode`), 60 digits, the same on both sides.
- halkyon_base: the register hands out accounts' public keys
(`public-keys-of`, local and remote); the table `shared_space_key`
and the endpoint `sharedSpaceKeys` (memberKeys, keysNeeded, seal,
mine). Only the Owner and Editors seal, the first key sealed to a
member stays, and a member's keys go with their membership. Every
host's migration creates the table.
- halkyon_app: `ContentCipher` seals a key to another account and
opens one sealed by another; `SharedKeys` shares a space's key with
its members, seals keys to newcomers and opens the person's own,
holding each member to the public keys `KeyPins` pinned on first
sight (`key_pins.json` in the person's folder), a change refused
until trusted anew.
Learn takes them into its `Privacy` in step 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 11:28:13 +01:00
|
|
|
// A member's public keys, as `SharedKeys` and `KeyPins` take them.
|
|
|
|
|
export 'package:halkyon_keys/halkyon_keys.dart' show PublicKeys;
|
|
|
|
|
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'l10n/halkyon_app_localizations.dart';
|
feat(base,app,learn,todo,train,meditate): agent accounts with tokens
ADR 0044's step 6a (D42, D43, D45): accounts for programs, such as AI
assistants, that an identified person makes from the Profile.
halkyon_base keeps them on the instance's register, which its members
ask: an agent is an account (agent_account: its owner, its name, its
public keys, set by itself once) with one token (agent_token: 32
random bytes, kept as their SHA-256 alone, shown once). agents.create,
mine, newToken, revoke, delete and setKeys serve its owner;
agentSignIn.signIn exchanges the token for an ordinary server-side
session of a day, with the scopes identified and agent, at most 60
sign-ins an hour from a network. Revoking a token or making a new one
ends the agent's sessions; a token unused for 90 days lapses in every
service's pruning. Up to 5 agents a person, where the operator didn't
switch them off (AGENTS=off; said on the privacy page and in
instance.basics). An agent makes no agents, creates or owns no shared
space, is invited and raised as a Viewer alone, is never handed a
space on, is named no operator and granted no assistants; each refusal
has its test. Deleting a person deletes their agents first; an agent's
public profile names whose agent it is.
In the apps, every Profile has an Agents card (halkyon_app's
AgentsSection): made with a name and a handle, its token shown once to
copy, a new token, the token taken back, deleted. Learn's Share sheet
marks an agent among the members and offers it no role above Viewer.
The migrations, the module's and each host's (Learn, Meditate, Todo,
Train), are additive alone: CREATE TABLE "agent_account" with a unique
index on "agentId" (one row an agent account) and one on "ownerId" (a
person's agents, and deleting them with the person), and CREATE TABLE
"agent_token" with unique indexes on "agentId" (one token an agent)
and "tokenHash" (the sign-in finds the token by its hash). No existing
table is touched, so the server before the deploy keeps working on the
migrated database.
Whats-New: Make agents in your Profile: accounts for programs such as AI assistants, which sign in with a token and see only what you share with them.
Whats-New-De: Leg in deinem Profil Agenten an: Konten für Programme wie KI-Assistenten, die sich mit einem Token anmelden und nur sehen, was du mit ihnen teilst.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 19:55:27 +01:00
|
|
|
export 'src/agent/bloc/agents_cubit.dart';
|
|
|
|
|
export 'src/agent/data/agent_gateway.dart';
|
|
|
|
|
export 'src/agent/data/agent_repository.dart';
|
|
|
|
|
export 'src/agent/data/serverpod_agent_gateway.dart';
|
|
|
|
|
export 'src/agent/model/agent.dart';
|
|
|
|
|
export 'src/agent/widget/agents_section.dart';
|
refactor(halkyon_app): the account section, About, the language and countries' names, shared
What Learn and Todo each held now comes from halkyon_app:
- AccountTiles: whose account on which instance, switching, signing
out (asking first for an anonymous account, as it deletes it) and
deleting it (asking first), the app's own words passed in.
- showAppAbout: the app's name, version, icon and legalese as it gives
them, the source of what the instance runs (AppDependencies gains
sourceCodeUrl), the app's own parts, the licence list.
- resolveAppLocale: the first preferred language the app speaks, never
by country alone, British English otherwise.
- AssetCountryNames, the default of InstanceGate and InstanceScope:
Unicode CLDR 48.2's territory names in English and German
(tool/country_names.py, 7.6 KB), Hong Kong, Macao, Myanmar and
Palestine by their short names as Learn's world map has them, so
Todo says "Germany (EU)" where it said "DE (EU)".
Learn's texts read as before in every language; its About bloc and
gateway, its country names from the world map and the keys only the
moved code used are gone, as are Todo's copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 23:51:52 +02:00
|
|
|
export 'src/app/about.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/app/app_frame.dart' show AppFrame, AppFrameScope, LookSettings;
|
refactor(halkyon_app): the account section, About, the language and countries' names, shared
What Learn and Todo each held now comes from halkyon_app:
- AccountTiles: whose account on which instance, switching, signing
out (asking first for an anonymous account, as it deletes it) and
deleting it (asking first), the app's own words passed in.
- showAppAbout: the app's name, version, icon and legalese as it gives
them, the source of what the instance runs (AppDependencies gains
sourceCodeUrl), the app's own parts, the licence list.
- resolveAppLocale: the first preferred language the app speaks, never
by country alone, British English otherwise.
- AssetCountryNames, the default of InstanceGate and InstanceScope:
Unicode CLDR 48.2's territory names in English and German
(tool/country_names.py, 7.6 KB), Hong Kong, Macao, Myanmar and
Palestine by their short names as Learn's world map has them, so
Todo says "Germany (EU)" where it said "DE (EU)".
Learn's texts read as before in every language; its About bloc and
gateway, its country names from the world map and the keys only the
moved code used are gone, as are Todo's copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 23:51:52 +02:00
|
|
|
export 'src/app/app_locale.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/app/app_scope.dart';
|
|
|
|
|
export 'src/app/instance_gate.dart';
|
|
|
|
|
export 'src/app/person_scope.dart';
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
export 'src/assistant/bloc/assistant_grant_cubit.dart';
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
export 'src/assistant/bloc/assistant_use_cubit.dart';
|
|
|
|
|
export 'src/assistant/bloc/assistants_cubit.dart';
|
|
|
|
|
export 'src/assistant/data/assistant_gateway.dart';
|
|
|
|
|
export 'src/assistant/data/assistant_messages.dart'
|
|
|
|
|
show
|
|
|
|
|
eventsOf,
|
|
|
|
|
failureOf,
|
|
|
|
|
llmMessageOf,
|
|
|
|
|
messageOf,
|
|
|
|
|
ownKeyFailureOf,
|
|
|
|
|
questionOf;
|
|
|
|
|
export 'src/assistant/data/assistant_repository.dart';
|
|
|
|
|
export 'src/assistant/data/serverpod_assistant_gateway.dart';
|
|
|
|
|
export 'src/assistant/model/assistant_amount.dart';
|
|
|
|
|
export 'src/assistant/model/assistant_failed.dart';
|
|
|
|
|
export 'src/assistant/model/assistant_grant.dart';
|
|
|
|
|
export 'src/assistant/model/assistant_offer.dart';
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
export 'src/assistant/model/assistant_provider.dart';
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
export 'src/assistant/model/own_assistant.dart';
|
feat(learn): a private studyset goes to an assistant only once agreed
ADR 0021, D9 and D17. Before a question sends a private studyset's
content to a remote model, through the parts chosen or a wider reach
with private studysets switched on, a warning names the studysets, the
provider's host and its terms (where checked: Anthropic, OpenAI,
Google, OpenRouter), and is asked once per studyset and assistant: the
agreement is kept in the person's sealed settings (schema 13,
privateAgreements, folded only where both rows hold it), shown in the
studyset's privacy dialog with Take back, and marked on the folded
button while it holds. Follow-ups and Try again ask again once taken
back, as the scope keeps where the parts came from (partsFrom). No
warning for an own assistant on this machine (loopback or localhost,
halkyon_llm's LlmEndpoint.onThisMachine); the local network may be
shared.
Texts that step 3 made untrue: the consent sheet now says what an
assistant looks up goes too and that conversations are kept encrypted
for the person's devices; the privacy dialogs no longer mention
showing a studyset to Claude; the privacy page says what is looked up
and when a private studyset goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 21:00:39 +01:00
|
|
|
export 'src/assistant/model/provider_terms.dart';
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
export 'src/assistant/widget/assistant_grants_screen.dart';
|
|
|
|
|
export 'src/assistant/widget/assistant_provider_view.dart';
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
export 'src/assistant/widget/assistant_use_view.dart';
|
|
|
|
|
export 'src/assistant/widget/assistant_words.dart';
|
|
|
|
|
export 'src/assistant/widget/assistants_card.dart';
|
2026-10-28 13:52:50 +01:00
|
|
|
export 'src/async/delayed_deletions.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/auth/bloc/session_bloc.dart';
|
|
|
|
|
export 'src/auth/bloc/sign_in_ways_cubit.dart';
|
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
|
|
|
export 'src/auth/bloc/wait_cubit.dart';
|
|
|
|
|
export 'src/auth/data/account_wait_store.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/auth/data/auth_gateway.dart';
|
|
|
|
|
export 'src/auth/data/auth_repository.dart';
|
|
|
|
|
export 'src/auth/data/serverpod_auth_gateway.dart';
|
|
|
|
|
export 'src/auth/data/sign_in_addresses.dart';
|
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
|
|
|
export 'src/auth/model/account_wait.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/auth/model/auth_status.dart';
|
feat(app): sign in with a password and TOTP on the device
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>
2026-10-20 15:42:45 +02:00
|
|
|
export 'src/auth/model/session_grant.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/auth/model/sign_in_ways.dart';
|
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
|
|
|
export 'src/auth/model/sign_ups.dart';
|
refactor(halkyon_app): the account section, About, the language and countries' names, shared
What Learn and Todo each held now comes from halkyon_app:
- AccountTiles: whose account on which instance, switching, signing
out (asking first for an anonymous account, as it deletes it) and
deleting it (asking first), the app's own words passed in.
- showAppAbout: the app's name, version, icon and legalese as it gives
them, the source of what the instance runs (AppDependencies gains
sourceCodeUrl), the app's own parts, the licence list.
- resolveAppLocale: the first preferred language the app speaks, never
by country alone, British English otherwise.
- AssetCountryNames, the default of InstanceGate and InstanceScope:
Unicode CLDR 48.2's territory names in English and German
(tool/country_names.py, 7.6 KB), Hong Kong, Macao, Myanmar and
Palestine by their short names as Learn's world map has them, so
Todo says "Germany (EU)" where it said "DE (EU)".
Learn's texts read as before in every language; its About bloc and
gateway, its country names from the world map and the keys only the
moved code used are gone, as are Todo's copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 23:51:52 +02:00
|
|
|
export 'src/auth/widget/account_tiles.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/auth/widget/session_gate.dart';
|
|
|
|
|
export 'src/auth/widget/start_screen.dart';
|
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
|
|
|
export 'src/auth/widget/wait_screen.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/base/base_client.dart';
|
refactor(app,learn): people's files on the device shared in halkyon_app
ADR 0043's recommendation, for Todo's attachments: what Learn had for its
files on the device moves into halkyon_app, unchanged but for its names,
and Learn uses it from there.
- FileCache, DirectoryFileCache (Learn's AssetFileCache): the files by
their SHA-256 in a folder of the person's, and which wait to be
uploaded.
- FileServerGateway, ServerpodFileServerGateway (Learn's
AssetServerGateway): halkyon_base's files endpoint, taking the module's
caller rather than Learn's client; FileLimitReached, FileRoom.
- Their fakes in halkyon_app's testing: MemoryFileCache,
FakeFileServerGateway, sha256Of.
- SealedFile: the format every app seals a file in (HLKF, version 1),
sealing with halkyon_app's ContentCipher; Learn's Privacy seals and
opens its files with it, for the same context as before, so every file
sealed so far opens as it did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 01:17:22 +01:00
|
|
|
export 'src/files/file_cache.dart';
|
feat(todo,app): files attached to tasks and comments
Halkyon's ADR 0043, D1, D3, D5 and D8: any file up to 100 MB, attached to
a task's description or to a comment, sealed as everything of Todo's.
Server: the attachment table, synced and sealed as the others, with the
sealed file's hash readable for the store; todoFiles hands halkyon_base
its rows, readable by their space's members, and takes sealed files
alone; the files no attachment names go daily. Its tests hold downloads
to the row's space, refuse a readable file and prune what no live row
names. Migration attachments: CREATE TABLE "attachment" with its indexes
and the foreign key to its space, cascading as every synced table's.
App: AttachmentRepository seals each file for its row, keeps it sealed
and opened in the person's folder, uploads it after each sync and fetches
it as it shows, both held back by Without files on mobile data but for a
file tapped. Under the description and a comment's text, a row of them:
pictures as thumbnails opening full-size, other files as chips opening in
the system's app, else saved; the paperclip beside the description and in the comment's
composer adds them, the composer holding them until it is sent. A task deleted takes its files
and its comments' along, a comment its own.
halkyon_app opens a file in the system's app (FileOpener): on Android
through its plugin and a FileProvider of its own class, sharing the
cache's halkyon_opened/ folder alone; on the desktop as a file: URI.
Whats-New: Attach files to a task's description or a comment with the paperclip: pictures show as thumbnails, other files open in their app.
Whats-New-De: Hänge mit der Büroklammer Dateien an die Beschreibung einer Aufgabe oder an einen Kommentar: Bilder erscheinen als Vorschau, andere Dateien öffnen sich in ihrer App.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 01:17:22 +01:00
|
|
|
export 'src/files/file_opener.dart';
|
feat(app): one Profile for every app, the person's own tab first, then each app's (ADR 0014)
The public profile was already one per person for every service of an
instance (the register, ADR 0016); its app side was Learn's alone. It
moves into halkyon_app: the model, the repository on the module's
endpoints, the form, the avatar drawn on the device and the sheet with
someone's profile. They take the Public prefix (PublicProfileRepository,
BasePublicProfileGateway), as Train's own ProfileRepository holds the
body's figures. AppDependencies gains profiles and files, the picker the
avatar is chosen with, which moves out of Learn too.
HalkyonProfile is every app's Profile screen: the person's own tab first,
the same in every app (the account and the ways they sign in, the app's
own cards, then the public profile), then the app's own tabs, folding into
the title. Learn adds its inbox and Statistics. Train adds Progress, Days
and, where nothing records, Plans, and so opens on the person's tab, no
longer on Progress. Todo, which has no Profile tab, opens it from a row at
the top of Browse, where Todoist keeps the account.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 09:44:38 +01:00
|
|
|
export 'src/files/file_picker.dart';
|
feat(app): sign in with a password and TOTP on the device
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>
2026-10-20 15:42:45 +02:00
|
|
|
export 'src/files/file_saver.dart';
|
refactor(app,learn): people's files on the device shared in halkyon_app
ADR 0043's recommendation, for Todo's attachments: what Learn had for its
files on the device moves into halkyon_app, unchanged but for its names,
and Learn uses it from there.
- FileCache, DirectoryFileCache (Learn's AssetFileCache): the files by
their SHA-256 in a folder of the person's, and which wait to be
uploaded.
- FileServerGateway, ServerpodFileServerGateway (Learn's
AssetServerGateway): halkyon_base's files endpoint, taking the module's
caller rather than Learn's client; FileLimitReached, FileRoom.
- Their fakes in halkyon_app's testing: MemoryFileCache,
FakeFileServerGateway, sha256Of.
- SealedFile: the format every app seals a file in (HLKF, version 1),
sealing with halkyon_app's ContentCipher; Learn's Privacy seals and
opens its files with it, for the same context as before, so every file
sealed so far opens as it did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 01:17:22 +01:00
|
|
|
export 'src/files/file_server_gateway.dart';
|
|
|
|
|
export 'src/files/serverpod_file_server_gateway.dart';
|
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>
2026-10-24 18:09:04 +02:00
|
|
|
export 'src/handle/bloc/handle_check_cubit.dart';
|
|
|
|
|
export 'src/handle/model/handle_problem.dart';
|
|
|
|
|
export 'src/handle/widget/handle_field.dart';
|
halkyon_app: data locations as precise as a country or a state
An operator's DATA_LOCATION and BACKUP_LOCATION may name a subdivision
(ISO 3166-2, US-CA, DE-BY) besides the EU or a country. The app adds
"(EU)" to a member country and the country to a subdivision ("Germany
(EU)", "California, United States", "Bavaria, Germany (EU)"), in
English and German, and shows the most specific flag there is: a
subdivision's own where the flag package has one, else its country's.
Subdivision names come from Unicode CLDR 48.2 (tool/subdivision_names.py),
bundled with their licence, which the app registers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 18:47:25 +02:00
|
|
|
export 'src/instance/cldr_license.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/instance/country_names.dart';
|
halkyon_app: data locations as precise as a country or a state
An operator's DATA_LOCATION and BACKUP_LOCATION may name a subdivision
(ISO 3166-2, US-CA, DE-BY) besides the EU or a country. The app adds
"(EU)" to a member country and the country to a subdivision ("Germany
(EU)", "California, United States", "Bavaria, Germany (EU)"), in
English and German, and shows the most specific flag there is: a
subdivision's own where the flag package has one, else its country's.
Subdivision names come from Unicode CLDR 48.2 (tool/subdivision_names.py),
bundled with their licence, which the app registers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 18:47:25 +02:00
|
|
|
export 'src/instance/european_union.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/instance/flag_icons_license.dart';
|
|
|
|
|
export 'src/instance/instance.dart';
|
|
|
|
|
export 'src/instance/instance_checker.dart';
|
|
|
|
|
export 'src/instance/instance_choice.dart';
|
|
|
|
|
export 'src/instance/instance_locations.dart';
|
|
|
|
|
export 'src/instance/instance_scope.dart';
|
|
|
|
|
export 'src/instance/instance_storage.dart';
|
|
|
|
|
export 'src/instance/instance_store.dart';
|
halkyon_app: data locations as precise as a country or a state
An operator's DATA_LOCATION and BACKUP_LOCATION may name a subdivision
(ISO 3166-2, US-CA, DE-BY) besides the EU or a country. The app adds
"(EU)" to a member country and the country to a subdivision ("Germany
(EU)", "California, United States", "Bavaria, Germany (EU)"), in
English and German, and shows the most specific flag there is: a
subdivision's own where the flag package has one, else its country's.
Subdivision names come from Unicode CLDR 48.2 (tool/subdivision_names.py),
bundled with their licence, which the app registers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 18:47:25 +02:00
|
|
|
export 'src/instance/subdivision_names.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/instance/widget/account_switcher_sheet.dart';
|
|
|
|
|
export 'src/instance/widget/first_start_screen.dart';
|
|
|
|
|
export 'src/instance/widget/instance_location.dart';
|
|
|
|
|
export 'src/instance/widget/instance_location_words.dart';
|
|
|
|
|
export 'src/instance/widget/instance_step.dart';
|
|
|
|
|
export 'src/instance/widget/instance_tile.dart';
|
feat(app,learn): the device's appearance and language, halkyon_app's, as Learn's Settings show them
The developer asked for Todo, Train and Meditate to offer Learn's
Appearance and Language (ADR 0014's amendment of 4 Nov 2026). Their
model, store, cubit and card move into halkyon_app: Appearance and Look
(a BCP 47 tag, null for the device's language), FileLookStore keeping it
in the app's support folder as JSON written by rename (per device, not
synced, read before the first frame), LookCubit for the apps to provide
above InstanceGate, and LookSettingsCard / DeviceLookCard with the texts
moved from Learn's arbs (look*, the same words). Each app passes the
languages it speaks, named in their own language.
Learn keeps its stored choice where it was, among its DeviceSettings,
and feeds the shared card from them: no migration, so nobody's language
or appearance moves, and nothing changes on its screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 22:30:29 +01:00
|
|
|
export 'src/look/bloc/look_cubit.dart';
|
|
|
|
|
export 'src/look/data/look_store.dart';
|
|
|
|
|
export 'src/look/model/look.dart';
|
|
|
|
|
export 'src/look/widget/look_settings_card.dart';
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
export 'src/operator/bloc/account_suggestions_cubit.dart';
|
feat(app,learn,todo): the operator's "This instance" card, screen and badge (ADR 0021, step 1)
Whoever operates the instance approves, refuses and suspends accounts
and names other operators in the app (D21 to D24, D34):
- halkyon_app: InstanceOperatorRepository on
ServerpodInstanceOperatorGateway (the module's instanceOperator
endpoint; refusals as OperatorFailed: notFound, isOperator,
lastOperator, notAllowed for 401 and 403, failed), in AppDependencies.
OperatingCubit counts the accounts that wait: asked as the person's
app opens, after every change, and every 5 minutes only while they
operate the instance, so nobody else's app keeps asking.
InstanceOperatorCard (or its tiles) and WaitingAccountsBadge show
nothing at all for anyone else. InstanceOperatorScreen, laid out as
Settings: the accounts that wait, each showing its four words to read
out before Approve (D33), Refuse for new ones after asking; who
operates, Take back after asking, the last always kept; Name an
operator by handle; Find an account by the start of its handle, with
Suspend after asking and Name as operator. English and German texts
("Admin" as the role word in German).
- Learn: the card right after the Sync card in Settings, which the
cogs' count leads to as their dot leads to Sync; the count on the
Settings cogs of Home and Profile.
- Todo: the tiles in the account sheet and the count on its button.
Todo's server is a member, whose `operating()` is always null, so
nothing shows there until it holds the register.
Tests: the gateway's mapping, the repository, both cubits with their
denials, the card, the badge and the screen; Learn's Settings shows the
card and the count to an operator and neither to anyone else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:55:28 +01:00
|
|
|
export 'src/operator/bloc/operating_cubit.dart';
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
export 'src/operator/bloc/operator_list_cubit.dart';
|
|
|
|
|
export 'src/operator/bloc/operator_overview_cubit.dart';
|
feat(app,learn,todo): the operator's "This instance" card, screen and badge (ADR 0021, step 1)
Whoever operates the instance approves, refuses and suspends accounts
and names other operators in the app (D21 to D24, D34):
- halkyon_app: InstanceOperatorRepository on
ServerpodInstanceOperatorGateway (the module's instanceOperator
endpoint; refusals as OperatorFailed: notFound, isOperator,
lastOperator, notAllowed for 401 and 403, failed), in AppDependencies.
OperatingCubit counts the accounts that wait: asked as the person's
app opens, after every change, and every 5 minutes only while they
operate the instance, so nobody else's app keeps asking.
InstanceOperatorCard (or its tiles) and WaitingAccountsBadge show
nothing at all for anyone else. InstanceOperatorScreen, laid out as
Settings: the accounts that wait, each showing its four words to read
out before Approve (D33), Refuse for new ones after asking; who
operates, Take back after asking, the last always kept; Name an
operator by handle; Find an account by the start of its handle, with
Suspend after asking and Name as operator. English and German texts
("Admin" as the role word in German).
- Learn: the card right after the Sync card in Settings, which the
cogs' count leads to as their dot leads to Sync; the count on the
Settings cogs of Home and Profile.
- Todo: the tiles in the account sheet and the count on its button.
Todo's server is a member, whose `operating()` is always null, so
nothing shows there until it holds the register.
Tests: the gateway's mapping, the repository, both cubits with their
denials, the card, the badge and the screen; Learn's Settings shows the
card and the count to an operator and neither to anyone else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:55:28 +01:00
|
|
|
export 'src/operator/data/instance_operator_gateway.dart';
|
|
|
|
|
export 'src/operator/data/instance_operator_repository.dart';
|
|
|
|
|
export 'src/operator/data/serverpod_instance_operator_gateway.dart';
|
|
|
|
|
export 'src/operator/model/operated_account.dart';
|
|
|
|
|
export 'src/operator/widget/instance_operator_card.dart';
|
|
|
|
|
export 'src/operator/widget/instance_operator_screen.dart';
|
test(app): the password screens, signed out and signed in
ADR 0017, step 3: widget tests driving the sheets as a person would,
on halkyon_app's fakes: signing in with a code (a wrong one refused
alike), an account never finished signing in with a code by mail and
setting up its authenticator (QR code, key, recovery codes saved), a
lost authenticator's reset, creating an account (the phrase shown and
three of its words checked before anything reaches the instance), the
recovery phrase getting back in, and the account section (a password
offered to someone anonymous; an authenticator added only with a code
from one before; the password changed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:46:43 +02:00
|
|
|
export 'src/password/bloc/account_recovery_cubit.dart';
|
|
|
|
|
export 'src/password/bloc/account_security_cubit.dart';
|
|
|
|
|
export 'src/password/bloc/account_setup_cubit.dart';
|
|
|
|
|
export 'src/password/bloc/password_sign_in_cubit.dart';
|
feat(app): sign in with a password and TOTP on the device
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>
2026-10-20 15:42:45 +02:00
|
|
|
export 'src/password/data/account_crypto.dart';
|
|
|
|
|
export 'src/password/data/account_key_store.dart';
|
|
|
|
|
export 'src/password/data/password_gateway.dart';
|
|
|
|
|
export 'src/password/data/password_repository.dart';
|
|
|
|
|
export 'src/password/data/serverpod_password_gateway.dart';
|
|
|
|
|
export 'src/password/data/sodium_account_crypto.dart';
|
|
|
|
|
export 'src/password/model/account_authenticator.dart';
|
|
|
|
|
export 'src/password/model/account_security.dart';
|
fix(auth): a code by mail serves only what it was asked for
Codes by mail were tied to nothing but their address (the re-review's
seventh finding): a hostile instance could answer that a setup never
finished, have the person type a code into it, relay it to start the real
instance's account reset, and 72 hours later relay a second to finish it
with its own password and keys; the mail said "to sign in" whatever the
code was for. A code is now asked for a purpose (signUp, finishSetup,
authenticatorReset, accountReset, deletion), only that purpose takes it (any
other is refused as no longer valid, leaving the code as it was), and its
mail says what it would do ("to reset your account on learn.halkyon.eu: a
new password and new keys, and what was encrypted with the old ones is
lost") and to type it only in the app on that instance, nowhere else. The
app asks for each code with the purpose of its step.
Migration (module, Learn's and Todo's servers, Learn's device schema):
ALTER TABLE "email_code_request" ADD COLUMN "purpose" text; nullable,
so the requests of the last 15 minutes before it stay, taken by nothing
and forgotten within the hour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 23:21:37 +02:00
|
|
|
export 'src/password/model/mail_code_purpose.dart';
|
feat(app): sign in with a password and TOTP on the device
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>
2026-10-20 15:42:45 +02:00
|
|
|
export 'src/password/model/password_failed.dart';
|
|
|
|
|
export 'src/password/model/phrase_check.dart';
|
|
|
|
|
export 'src/password/model/unlocked_account_keys.dart';
|
test(app): the password screens, signed out and signed in
ADR 0017, step 3: widget tests driving the sheets as a person would,
on halkyon_app's fakes: signing in with a code (a wrong one refused
alike), an account never finished signing in with a code by mail and
setting up its authenticator (QR code, key, recovery codes saved), a
lost authenticator's reset, creating an account (the phrase shown and
three of its words checked before anything reaches the instance), the
recovery phrase getting back in, and the account section (a password
offered to someone anonymous; an authenticator added only with a code
from one before; the password changed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:46:43 +02:00
|
|
|
export 'src/password/widget/account_recovery_sheet.dart';
|
|
|
|
|
export 'src/password/widget/account_security_section.dart';
|
|
|
|
|
export 'src/password/widget/account_setup_sheet.dart';
|
|
|
|
|
export 'src/password/widget/password_sign_in_sheet.dart';
|
|
|
|
|
export 'src/password/widget/password_texts.dart';
|
|
|
|
|
export 'src/password/widget/password_widgets.dart';
|
2026-11-03 22:07:35 +01:00
|
|
|
export 'src/password/widget/secret_clipboard.dart';
|
2026-10-21 15:09:02 +02:00
|
|
|
export 'src/password/widget/secret_screen.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/person/person_folders.dart';
|
feat(app): content sealed with data keys, and an anonymous person's device keys (ADR 0019)
`ContentCipher` makes a person's data keys, sealed to their account and
signed by it, opens them, and seals and opens a row's content with them for
the row it is stored in (`SodiumContentCipher`, on the isolate it is used
on, as rows are read and written one by one; `FakeContentCipher` for
tests). A sealed payload holds the signature, then the ciphertext.
`AccountCrypto.newDeviceKeys` makes the key pairs an anonymous person's
device encrypts their private studysets with (ADR 0017, point 9, not built
before); setting a password keeps them as the account's
(`keepingKeys` with a new phrase), so nothing they made private is lost.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-21 20:58:19 +02:00
|
|
|
export 'src/privacy/content_cipher.dart';
|
2026-10-28 11:08:46 +01:00
|
|
|
export 'src/privacy/payload_envelope.dart';
|
refactor(app,learn): people's files on the device shared in halkyon_app
ADR 0043's recommendation, for Todo's attachments: what Learn had for its
files on the device moves into halkyon_app, unchanged but for its names,
and Learn uses it from there.
- FileCache, DirectoryFileCache (Learn's AssetFileCache): the files by
their SHA-256 in a folder of the person's, and which wait to be
uploaded.
- FileServerGateway, ServerpodFileServerGateway (Learn's
AssetServerGateway): halkyon_base's files endpoint, taking the module's
caller rather than Learn's client; FileLimitReached, FileRoom.
- Their fakes in halkyon_app's testing: MemoryFileCache,
FakeFileServerGateway, sha256Of.
- SealedFile: the format every app seals a file in (HLKF, version 1),
sealing with halkyon_app's ContentCipher; Learn's Privacy seals and
opens its files with it, for the same context as before, so every file
sealed so far opens as it did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 01:17:22 +01:00
|
|
|
export 'src/privacy/sealed_file.dart';
|
feat(app): one Profile for every app, the person's own tab first, then each app's (ADR 0014)
The public profile was already one per person for every service of an
instance (the register, ADR 0016); its app side was Learn's alone. It
moves into halkyon_app: the model, the repository on the module's
endpoints, the form, the avatar drawn on the device and the sheet with
someone's profile. They take the Public prefix (PublicProfileRepository,
BasePublicProfileGateway), as Train's own ProfileRepository holds the
body's figures. AppDependencies gains profiles and files, the picker the
avatar is chosen with, which moves out of Learn too.
HalkyonProfile is every app's Profile screen: the person's own tab first,
the same in every app (the account and the ways they sign in, the app's
own cards, then the public profile), then the app's own tabs, folding into
the title. Learn adds its inbox and Statistics. Train adds Progress, Days
and, where nothing records, Plans, and so opens on the person's tab, no
longer on Progress. Todo, which has no Profile tab, opens it from a row at
the top of Browse, where Todoist keeps the account.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 09:44:38 +01:00
|
|
|
export 'src/profile/bloc/public_profile_bloc.dart';
|
|
|
|
|
export 'src/profile/data/avatar_image.dart';
|
|
|
|
|
export 'src/profile/data/base_public_profile_gateway.dart';
|
|
|
|
|
export 'src/profile/data/public_profile_gateway.dart';
|
|
|
|
|
export 'src/profile/data/public_profile_repository.dart';
|
|
|
|
|
export 'src/profile/model/public_profile.dart';
|
|
|
|
|
export 'src/profile/widget/halkyon_profile.dart';
|
|
|
|
|
export 'src/profile/widget/profile_sheet.dart';
|
|
|
|
|
export 'src/profile/widget/public_profile_section.dart';
|
feat(base,app,keys): keys of shared spaces, sealed to each member
Step 3 of ADR 0044 (D3, D18, D19). A shared space's data keys are
sealed on a device to each member's public keys; the instance keeps
what devices sealed and never opens it.
- halkyon_keys: the safety code of two accounts' public keys
(`safetyCode`), 60 digits, the same on both sides.
- halkyon_base: the register hands out accounts' public keys
(`public-keys-of`, local and remote); the table `shared_space_key`
and the endpoint `sharedSpaceKeys` (memberKeys, keysNeeded, seal,
mine). Only the Owner and Editors seal, the first key sealed to a
member stays, and a member's keys go with their membership. Every
host's migration creates the table.
- halkyon_app: `ContentCipher` seals a key to another account and
opens one sealed by another; `SharedKeys` shares a space's key with
its members, seals keys to newcomers and opens the person's own,
holding each member to the public keys `KeyPins` pinned on first
sight (`key_pins.json` in the person's folder), a change refused
until trusted anew.
Learn takes them into its `Privacy` in step 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 11:28:13 +01:00
|
|
|
export 'src/shared_space/key_pins.dart';
|
|
|
|
|
export 'src/shared_space/serverpod_shared_keys_gateway.dart';
|
feat(app,learn): shared spaces' members on the device
Step 4 (e) of ADR 0044, its first part, for every app's screens.
- halkyon_app: `SharedSpaceGateway` on the module's `sharedSpace`
endpoint (the spaces, their members and roles, inviting, handing on,
leaving), refusals as `SpaceRefused`; `SharedKeys.trustIn` gives
each member's safety code and whether it was compared or their keys
changed, `markCompared`, and `trustNewKeys`, which pins the keys
whose code was compared and never those the instance hands next.
`FakeSharedSpaceServer` keeps the module's rules for tests, in step
with `FakeSharedKeysServer`, which now knows members' handles.
- Learn's sharing makes its space through it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 14:39:12 +01:00
|
|
|
export 'src/shared_space/serverpod_shared_space_gateway.dart';
|
feat(base,app,keys): keys of shared spaces, sealed to each member
Step 3 of ADR 0044 (D3, D18, D19). A shared space's data keys are
sealed on a device to each member's public keys; the instance keeps
what devices sealed and never opens it.
- halkyon_keys: the safety code of two accounts' public keys
(`safetyCode`), 60 digits, the same on both sides.
- halkyon_base: the register hands out accounts' public keys
(`public-keys-of`, local and remote); the table `shared_space_key`
and the endpoint `sharedSpaceKeys` (memberKeys, keysNeeded, seal,
mine). Only the Owner and Editors seal, the first key sealed to a
member stays, and a member's keys go with their membership. Every
host's migration creates the table.
- halkyon_app: `ContentCipher` seals a key to another account and
opens one sealed by another; `SharedKeys` shares a space's key with
its members, seals keys to newcomers and opens the person's own,
holding each member to the public keys `KeyPins` pinned on first
sight (`key_pins.json` in the person's folder), a change refused
until trusted anew.
Learn takes them into its `Privacy` in step 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 11:28:13 +01:00
|
|
|
export 'src/shared_space/shared_keys.dart';
|
|
|
|
|
export 'src/shared_space/shared_keys_gateway.dart';
|
feat(app,learn): shared spaces' members on the device
Step 4 (e) of ADR 0044, its first part, for every app's screens.
- halkyon_app: `SharedSpaceGateway` on the module's `sharedSpace`
endpoint (the spaces, their members and roles, inviting, handing on,
leaving), refusals as `SpaceRefused`; `SharedKeys.trustIn` gives
each member's safety code and whether it was compared or their keys
changed, `markCompared`, and `trustNewKeys`, which pins the keys
whose code was compared and never those the instance hands next.
`FakeSharedSpaceServer` keeps the module's rules for tests, in step
with `FakeSharedKeysServer`, which now knows members' handles.
- Learn's sharing makes its space through it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 14:39:12 +01:00
|
|
|
export 'src/shared_space/shared_space_gateway.dart';
|
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
|
|
|
export 'src/simplelogin/bloc/simplelogin_cubit.dart';
|
2026-10-24 11:56:26 +02:00
|
|
|
export 'src/simplelogin/bloc/simplelogin_standing_cubit.dart';
|
feat(app): signing in with SimpleLogin on the device, its data layer (ADR 0020)
SimpleLoginRepository signs in with SimpleLogin in the browser
(OidcBrowser: the app's scheme on Android, iOS and macOS, the app's port
on the loopback on Linux and Windows, D9), then makes the account's keys
and signs someone new up, opens an account's keys with the password, gives
an account of before its first keys, or links a new SimpleLogin account in
place of a lost one with the recovery phrase (D7), which opens the keys on
the device first: a phrase that doesn't fit sends no proof, and no
password is asked after. An anonymous person adding SimpleLogin keeps the
keys their device holds. Nothing is the device's until `enter`.
SimpleLoginGateway calls halkyon_base's `simpleLogin` and `keys`
endpoints (ServerpodSimpleLoginGateway), with a fake for tests, as does
FakeOidcBrowser. The calls made on clients of their own (signed out, or as
a session not yet the device's) move from the password gateway into
BaseCalls, which both share.
Fixes BaseProtocol, the client of halkyon_base alone, which didn't read
halkyon_oidc's classes: every refusal of SimpleLogin's sign-in on a
signed-out call would have reached the app as a failure. OidcBrowser keeps
its loopback private, so that a fake can implement it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 00:17:04 +02:00
|
|
|
export 'src/simplelogin/data/serverpod_simplelogin_gateway.dart';
|
|
|
|
|
export 'src/simplelogin/data/simplelogin_gateway.dart';
|
|
|
|
|
export 'src/simplelogin/data/simplelogin_repository.dart';
|
|
|
|
|
export 'src/simplelogin/model/simplelogin_arrival.dart';
|
|
|
|
|
export 'src/simplelogin/model/simplelogin_failed.dart';
|
2026-10-24 11:56:26 +02:00
|
|
|
export 'src/simplelogin/model/simplelogin_standing.dart';
|
feat(app): a reset of the keys without the phrase, its banner, and the keys' proof key for a password that signs in (ADR 0020, D31–D35)
SimpleLogin's sheet, its password and its recovery phrase both lost:
under the lost-password step's phrase field, Lost your recovery phrase
too? says what stays and what is lost and asks for new keys
(`keys.startReset`) with the sign-in of a moment ago, which the device
then drops. The next sign-in says when while it waits, and once due
offers new keys, the sign-up's own password and phrase steps
(`keys.completeReset`), or the password, which calls it off (D31).
Every app's home shows `KeysResetBanner` where "too old" shows, and the
account section's SimpleLogin row the same words (`KeysResetNotice`),
each with Call it off, the password's proof alone
(`keys.callOffResets`, D32), and, once due on a device that holds the
keys, Finish here, which wraps the same keys anew (D33, D35). Both show
only a reset of an account that signs in with SimpleLogin alone; ADR
0017's resets show in its own section, which now shares `AlertBox` with
them.
An account whose password signs in has no Forgot your password? there,
and is asked its password and a code once while its keys lack the proof
key, at a sign-in or on adding SimpleLogin (`password.setProofKey`,
D34). `PasswordUnlock` carries the proof key, which its forgetting
leaves, and the keys' password proves for a purpose given.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 22:37:17 +02:00
|
|
|
export 'src/simplelogin/widget/keys_reset.dart';
|
2026-10-24 11:56:26 +02:00
|
|
|
export 'src/simplelogin/widget/simplelogin_section.dart';
|
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
|
|
|
export 'src/simplelogin/widget/simplelogin_sheet.dart';
|
2026-10-24 11:56:26 +02:00
|
|
|
export 'src/simplelogin/widget/simplelogin_standing_scope.dart';
|
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
|
|
|
export 'src/simplelogin/widget/simplelogin_texts.dart';
|
refactor(app): the device's speech recognition moves from Learn into halkyon_app, with dictation into a text
Learn's `SpeechInput` and `DeviceSpeechInput` (ADR 0021, D67) are
halkyon_app's now, and its `FakeSpeechInput` is in `testing.dart`, so
Meditate's dream journal (ADR 0038, D15) listens through the same code.
Learn imports them from there and behaves as before: its composer and
its tests are unchanged but for the imports, and `DeviceSpeechInput()`
asks for what it asked before.
`DeviceSpeechInput` takes three options, off by default:
- `preferOnDevice`: on Android, recognition on the device where it can
(Android 12 on), else offline; should that fail before a word is
heard (not a pause), it listens as Android otherwise does from then
on.
- `pauseFor`: how long a pause may last before the recogniser stops.
- `listenFor`: how long one listen may last at most.
`Dictation` writes what is heard where the cursor stands
(`DictationInsertion`: in place of a selection, set apart by a space),
shows the words so far, and listens again after each pause while it is
on, stopping by itself after three listens running heard nothing.
speech_to_text moves from Learn's pubspec to halkyon_app's, the lock
unchanged. Todo, Train and Meditate link the plugin now; their
generated plugin registrants (macOS, Windows) say so. An app declares
the microphone itself, and Todo and Train declare none, so nothing
there can listen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 14:41:07 +01:00
|
|
|
export 'src/speech/dictation.dart';
|
|
|
|
|
export 'src/speech/speech_input.dart';
|
feat(learn,todo,meditate): dictate through the phone's voice input app (ADR 0038, D18)
On Android the microphone now opens the app handling
RECOGNIZE_SPEECH (FUTO Voice Input, Google's), the default the person
chose in Android, and puts the text it returns where the cursor stood,
as Dictation does; cancelled, nothing. Only where none is installed, or
it can't be opened, does speech_to_text listen in line as before.
halkyon_app: VoiceInputApp (one listen, an answer once closed) on the
channel app.halkyon/voice_input, which DeviceSpeechInput asks first at
every listen, answering SpeechStart.finished so that Dictation doesn't
listen again; speech_to_text isn't touched on that route, so our app
asks for no microphone. SpeechInput.listen and Dictation.start take the
prompt the voice input app shows (the field's hint).
Each app: VoiceInput.kt (queryIntentActivities, startActivityForResult,
the first of EXTRA_RESULTS) attached in MainActivity, which hands it
activity results, and RECOGNIZE_SPEECH among the manifest's queries.
The privacy pages of Learn and Meditate say so.
Our own Whisper and Meditate's dream recordings are dropped (D18).
Whats-New: The microphone now dictates through your phone's voice input app, such as FUTO Voice Input.
Whats-New-De: Das Mikrofon diktiert jetzt über die Spracheingabe-App deines Telefons, etwa FUTO Voice Input.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-07 23:45:20 +01:00
|
|
|
export 'src/speech/voice_input_app.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/sync/app_compatibility.dart';
|
|
|
|
|
export 'src/sync/bloc/sync_status_bloc.dart';
|
feat(app,ui): sync waits on mobile data and asks first
The developer's request of 10 Nov 2026, decided in ADR 0042: on mobile
data every app holds its sync back, rows and files alike, and asks.
halkyon_app becomes an Android plugin, its first native code, registered
in every app by Flutter: HalkyonAppPlugin.java says which network the
device is on and whether it is metered as Android has it (no
NET_CAPABILITY_NOT_METERED: cellular, a phone's hotspot, Wi-Fi marked
metered). Java, so it applies no Kotlin Gradle Plugin.
MobileDataGate, one per device, holds the setting (Ask first, Sync
without files, Sync everything; kept in <app support>/mobile_data.json),
the network, and the person's answer, which holds until the device moves
to another network or off a metered one. SyncService asks it before each
sync: held back, nothing goes, the status counts what waits and asks; an
answer or Wi-Fi syncs at once, and syncing now while held back is Sync
now. Each app's frame shows the banner above every tab, in halkyon_ui's
new NavigationFrame.banner slot (On mobile data · 3 changes waiting, with
Not now, Without files and Sync now); the Sync card in Settings chooses
the setting on a phone and says when files wait for Wi-Fi. Learn's files
neither go up nor come down while held back.
Whats-New: On mobile data the app no longer syncs by itself: a banner asks first, and Settings › Sync chooses whether to ask, sync without files, or sync everything.
Whats-New-De: Mit mobilen Daten synchronisiert die App nicht mehr von selbst: Ein Hinweis fragt zuerst, und unter Einstellungen › Synchronisierung wählst du, ob sie fragt, ohne Dateien oder alles synchronisiert.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-10 23:38:17 +01:00
|
|
|
export 'src/sync/network/mobile_data_gate.dart';
|
|
|
|
|
export 'src/sync/network/network_watch.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/sync/pending_changes_store.dart';
|
|
|
|
|
export 'src/sync/sync_service.dart';
|
|
|
|
|
export 'src/sync/sync_status.dart';
|
|
|
|
|
export 'src/sync/widget/app_mismatch_banner.dart';
|
feat(app,ui): sync waits on mobile data and asks first
The developer's request of 10 Nov 2026, decided in ADR 0042: on mobile
data every app holds its sync back, rows and files alike, and asks.
halkyon_app becomes an Android plugin, its first native code, registered
in every app by Flutter: HalkyonAppPlugin.java says which network the
device is on and whether it is metered as Android has it (no
NET_CAPABILITY_NOT_METERED: cellular, a phone's hotspot, Wi-Fi marked
metered). Java, so it applies no Kotlin Gradle Plugin.
MobileDataGate, one per device, holds the setting (Ask first, Sync
without files, Sync everything; kept in <app support>/mobile_data.json),
the network, and the person's answer, which holds until the device moves
to another network or off a metered one. SyncService asks it before each
sync: held back, nothing goes, the status counts what waits and asks; an
answer or Wi-Fi syncs at once, and syncing now while held back is Sync
now. Each app's frame shows the banner above every tab, in halkyon_ui's
new NavigationFrame.banner slot (On mobile data · 3 changes waiting, with
Not now, Without files and Sync now); the Sync card in Settings chooses
the setting on a phone and says when files wait for Wi-Fi. Learn's files
neither go up nor come down while held back.
Whats-New: On mobile data the app no longer syncs by itself: a banner asks first, and Settings › Sync chooses whether to ask, sync without files, or sync everything.
Whats-New-De: Mit mobilen Daten synchronisiert die App nicht mehr von selbst: Ein Hinweis fragt zuerst, und unter Einstellungen › Synchronisierung wählst du, ob sie fragt, ohne Dateien oder alles synchronisiert.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-10 23:38:17 +01:00
|
|
|
export 'src/sync/widget/mobile_data_banner.dart';
|
halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.
An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 15:18:49 +02:00
|
|
|
export 'src/sync/widget/sync_and_wait.dart';
|
|
|
|
|
export 'src/sync/widget/sync_status_card.dart';
|
|
|
|
|
export 'src/sync/widget/sync_trouble_badge.dart';
|
feat(app): what's new after an update, drafted by the release tool (ADR 0030, D61)
halkyon_app's WhatsNew reads what each release changed from the app's
assets/whats_new/ (<version>.<language>.txt, English and German, a change
a line): after an update a sheet lists every release since the one the
device showed last, newest first, once; Settings shows the running
version's again, above About. Nothing on a fresh install.
tool/release.dart drafts the missing texts from the commits that count
after "Go on?" (German marked TRANSLATE:), opens them in the editor until
they fit (no mark left, F-Droid's 500 characters) and commits them with
the version. The English is the Forgejo notes' What's new, each language
F-Droid's changelog of the build, written at publishing and copied in by
tool/fdroid_repo.sh. Learn and Train bundle their texts, Learn's 0.22.0
and Train's 0.1.0 written to start.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 19:36:20 +01:00
|
|
|
export 'src/whats_new/whats_new.dart';
|
|
|
|
|
export 'src/whats_new/whats_new_view.dart';
|