docs(store): say studysets are the learners' own and instances anyone's
The Snap Store and F-Droid listings now say what the demo video says: every studyset is made by its learners (build one for any subject, or copy one someone published), the app connects to any instance, the repository holds everything to run one, and all the code is public (app AGPL-3.0, server SSPL-1.0). Signing in later keeps everything with GitHub or an email address, now that both are ways in. The F-Droid listing takes the snap's description again, as its header says; it had kept the text from before v0.17.0. AGENTS.md no longer says it declares TetheredNet, which it dropped once the app could choose its instance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
d3ece5c528
commit
19347ef4fc
3 changed files with 30 additions and 11 deletions
|
|
@ -128,7 +128,7 @@ A spaced-repetition learning app: studysets of flashcards, reviewed on an FSRS s
|
||||||
- **A green `main` deploys learn.halkyon.eu** (paused until the hackathon's judging ends on 22 Oct 2026: the judges' v0.17.0 uses the instance, which stays at v0.17.0 meanwhile, while `main` moves on and is tested locally; CI's deploy job is `if: false` until then, and no release goes out, as its app would need a server it doesn't have) through `deploy/redeploy.sh`, the only command the runner's key may run as `deploy` (pinned in `authorized_keys`, from that server itself only). It deploys the commit CI checked while it is still the tip, as a fast-forward, only if every new commit is signed by a key in `deploy/allowed-signers` (the deployed copy decides), after dumping the database (the last 3 dumps stay in `~deploy/backups`: the way back from a deploy, not backups), applies the migrations in a run of their own before switching (`--role=maintenance --apply-migrations`: a failing migration stops the deploy while the old server keeps serving, so migrations must keep the old server working until the switch), and fails unless the new server answers. A failed deploy leaves the checkout at the deployed commit; run the workflow again to retry. By hand: `ssh sahaquiel-deploy halkyon-learn/deploy/redeploy.sh <commit>`. Never `docker compose down -v` there.
|
- **A green `main` deploys learn.halkyon.eu** (paused until the hackathon's judging ends on 22 Oct 2026: the judges' v0.17.0 uses the instance, which stays at v0.17.0 meanwhile, while `main` moves on and is tested locally; CI's deploy job is `if: false` until then, and no release goes out, as its app would need a server it doesn't have) through `deploy/redeploy.sh`, the only command the runner's key may run as `deploy` (pinned in `authorized_keys`, from that server itself only). It deploys the commit CI checked while it is still the tip, as a fast-forward, only if every new commit is signed by a key in `deploy/allowed-signers` (the deployed copy decides), after dumping the database (the last 3 dumps stay in `~deploy/backups`: the way back from a deploy, not backups), applies the migrations in a run of their own before switching (`--role=maintenance --apply-migrations`: a failing migration stops the deploy while the old server keeps serving, so migrations must keep the old server working until the switch), and fails unless the new server answers. A failed deploy leaves the checkout at the deployed commit; run the workflow again to retry. By hand: `ssh sahaquiel-deploy halkyon-learn/deploy/redeploy.sh <commit>`. Never `docker compose down -v` there.
|
||||||
- **Sidecars** (`deploy/sidecars/README.md`, decided 2026-10-14): services an instance may run beside itself, each a compose file of its own beside `deploy/compose.yaml` (`compose.jellyfin.yaml`; later LanguageTool, Fedify), switched on in `deploy/.env` (`COMPOSE_FILE=compose.yaml:compose.jellyfin.yaml`), so `redeploy.sh` deploys them with the instance. The instance's compose has two networks: `edge` (`halkyon-learn-edge`, 172.30.80.0/24), Caddy's at a fixed address (172.30.80.2), shared with the server, and the internal default one, where the server reaches Postgres; a sidecar joins `edge` alone and publishes no port (the CI runner may reach `127.0.0.1`, never Docker's 172.16.0.0/12). A sidecar reached from outside mounts its site from `deploy/sidecars/` into Caddy's `/etc/caddy/sidecars/`, which the Caddyfile imports (none without), and `redeploy.sh` recreates Caddy when a site changes. Each runs as a user ID of its own (Jellyfin 61096), never one a person, another container or the runner uses; its files on the server are git-ignored (`deploy/jellyfin/`). The Jellyfin sidecar sets the server's `JELLYFIN_URL`, so the instance suggests it.
|
- **Sidecars** (`deploy/sidecars/README.md`, decided 2026-10-14): services an instance may run beside itself, each a compose file of its own beside `deploy/compose.yaml` (`compose.jellyfin.yaml`; later LanguageTool, Fedify), switched on in `deploy/.env` (`COMPOSE_FILE=compose.yaml:compose.jellyfin.yaml`), so `redeploy.sh` deploys them with the instance. The instance's compose has two networks: `edge` (`halkyon-learn-edge`, 172.30.80.0/24), Caddy's at a fixed address (172.30.80.2), shared with the server, and the internal default one, where the server reaches Postgres; a sidecar joins `edge` alone and publishes no port (the CI runner may reach `127.0.0.1`, never Docker's 172.16.0.0/12). A sidecar reached from outside mounts its site from `deploy/sidecars/` into Caddy's `/etc/caddy/sidecars/`, which the Caddyfile imports (none without), and `redeploy.sh` recreates Caddy when a site changes. Each runs as a user ID of its own (Jellyfin 61096), never one a person, another container or the runner uses; its files on the server are git-ignored (`deploy/jellyfin/`). The Jellyfin sidecar sets the server's `JELLYFIN_URL`, so the instance suggests it.
|
||||||
- **Backups** (`deploy/backups.md`): every hour, `deploy/backup.sh` (a systemd timer, as `deploy`) dumps the database, which holds people's files too, then bundles each published studyset's repository (`git bundle`, from the server's `repositories` volume), and restic stores both in a Hetzner Storage Box (24 hourly, 7 daily, 4 weekly, 3 monthly); the box's own snapshots protect it from a taken-over server. A restore takes the instance back up to an hour, and people who started since lose what their device held: their app no longer finds its session, starts a new anonymous account and deletes the old folder. So a change that makes devices lose their session, or keeps deleted people's data longer, changes what a restore costs; say so in `deploy/backups.md`.
|
- **Backups** (`deploy/backups.md`): every hour, `deploy/backup.sh` (a systemd timer, as `deploy`) dumps the database, which holds people's files too, then bundles each published studyset's repository (`git bundle`, from the server's `repositories` volume), and restic stores both in a Hetzner Storage Box (24 hourly, 7 daily, 4 weekly, 3 monthly); the box's own snapshots protect it from a taken-over server. A restore takes the instance back up to an hour, and people who started since lose what their device held: their app no longer finds its session, starts a new anonymous account and deletes the old folder. So a change that makes devices lose their session, or keeps deleted people's data longer, changes what a restore costs; say so in `deploy/backups.md`.
|
||||||
- **Releases** are the project's, not an instance's: the release, the Snap Store listing and the F-Droid listing point to the project's page on Halkyon's website, halkyon.app/learn (its own repository, `project-halkyon/website`; learn.halkyon.app redirects there); the builds name learn.halkyon.eu, the project's instance (`SERVER_URL`), which a new install offers first (see *Instances*). They come from a developer's laptop, never from CI: `tool/release.sh` on `main` as pushed and green in CI. Versions follow semantic versioning, worked out from the commits since the last release (`feat` raises the minor number, `fix`/`perf` the patch number, breaking ones the major from 1.0 on; other types release nothing), so **commit messages keep their type prefix**. It writes the version and a raised build number into the app's pubspec in a signed release commit (`chore(release): vX.Y.Z`) with a signed tag (a key in `deploy/allowed-signers`), builds one universal APK from a checkout of the tag, signed with the app's key, and the snap (`snap/snapcraft.yaml`, the committed HEAD, built in LXD), both with `SERVER_URL=https://learn.halkyon.eu/`; signs `SHA256SUMS` with GPG; pushes only once both builds passed their checks; and publishes on code.halkyon.app through Forgejo's API and in the F-Droid repository. The Snap Store upload is off until `snap_store_channel` in the script names a channel. A run that stopped after its release commit and tag carries on with them. The key's password and the Forgejo token are asked for at each run and never stored. The app's key is a PKCS #12 keystore outside the repository (also in the maintainer's password manager); there is no Google Play, so it is the app's identity: lost, installed apps can't be updated. Its certificate's SHA-256 is in `tool/release/android-certificate.sha256`, and the script releases no APK signed otherwise; other release builds fall back to the debug key (`android/app/build.gradle.kts`), so `flutter run --release` works anywhere. The APK also goes into the project's own F-Droid repository, https://halkyon.app/fdroid/repo, through `tool/fdroid_repo.sh` (fdroidserver v2.4.5): its settings and the app's listing are in `tool/release/fdroid/` (the anti-features TetheredNet and NonFreeNet declared with their reasons and F-Droid's own icons); its index is signed with a key of its own (a PKCS #12 keystore beside the app's, password asked for, certificate pinned in `tool/release/fdroid-index-certificate.sha256`, since a new key makes the F-Droid app refuse the repository); and it is uploaded to the halkyon.app server (`ssh ramiel-fdroid`) as the user `fdroid`, whose key can only write into the folder Caddy serves (rrsync), the APKs first and the indexes last.
|
- **Releases** are the project's, not an instance's: the release, the Snap Store listing and the F-Droid listing point to the project's page on Halkyon's website, halkyon.app/learn (its own repository, `project-halkyon/website`; learn.halkyon.app redirects there); the builds name learn.halkyon.eu, the project's instance (`SERVER_URL`), which a new install offers first (see *Instances*). They come from a developer's laptop, never from CI: `tool/release.sh` on `main` as pushed and green in CI. Versions follow semantic versioning, worked out from the commits since the last release (`feat` raises the minor number, `fix`/`perf` the patch number, breaking ones the major from 1.0 on; other types release nothing), so **commit messages keep their type prefix**. It writes the version and a raised build number into the app's pubspec in a signed release commit (`chore(release): vX.Y.Z`) with a signed tag (a key in `deploy/allowed-signers`), builds one universal APK from a checkout of the tag, signed with the app's key, and the snap (`snap/snapcraft.yaml`, the committed HEAD, built in LXD), both with `SERVER_URL=https://learn.halkyon.eu/`; signs `SHA256SUMS` with GPG; pushes only once both builds passed their checks; and publishes on code.halkyon.app through Forgejo's API and in the F-Droid repository. The Snap Store upload is off until `snap_store_channel` in the script names a channel. A run that stopped after its release commit and tag carries on with them. The key's password and the Forgejo token are asked for at each run and never stored. The app's key is a PKCS #12 keystore outside the repository (also in the maintainer's password manager); there is no Google Play, so it is the app's identity: lost, installed apps can't be updated. Its certificate's SHA-256 is in `tool/release/android-certificate.sha256`, and the script releases no APK signed otherwise; other release builds fall back to the debug key (`android/app/build.gradle.kts`), so `flutter run --release` works anywhere. The APK also goes into the project's own F-Droid repository, https://halkyon.app/fdroid/repo, through `tool/fdroid_repo.sh` (fdroidserver v2.4.5): its settings and the app's listing are in `tool/release/fdroid/` (the anti-feature NonFreeNet declared with its reasons and F-Droid's own icon; not TetheredNet, as the app chooses its instance); its index is signed with a key of its own (a PKCS #12 keystore beside the app's, password asked for, certificate pinned in `tool/release/fdroid-index-certificate.sha256`, since a new key makes the F-Droid app refuse the repository); and it is uploaded to the halkyon.app server (`ssh ramiel-fdroid`) as the user `fdroid`, whose key can only write into the folder Caddy serves (rrsync), the APKs first and the indexes last.
|
||||||
|
|
||||||
## Rules the code relies on
|
## Rules the code relies on
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ adopt-info: halkyon-learn # the version, from the app's pubspec.yaml
|
||||||
summary: Learn with flashcards, studied offline and synced between your devices
|
summary: Learn with flashcards, studied offline and synced between your devices
|
||||||
description: |
|
description: |
|
||||||
Halkyon Learn is a spaced-repetition app for your own material: studysets of
|
Halkyon Learn is a spaced-repetition app for your own material: studysets of
|
||||||
flashcards, notes, learning paths and PDFs, reviewed on an FSRS schedule. It
|
flashcards, notes, learning paths and PDFs, reviewed on an FSRS schedule. Every
|
||||||
works offline and syncs between your devices through an instance anyone can
|
studyset is made by its learners: build yours for any subject, or copy one that
|
||||||
run.
|
someone published. It works offline and syncs between your devices.
|
||||||
|
|
||||||
Learn from what you watch: play films and shows from your Jellyfin server,
|
Learn from what you watch: play films and shows from your Jellyfin server,
|
||||||
and every subtitle line is split into dictionary words, with readings above
|
and every subtitle line is split into dictionary words, with readings above
|
||||||
|
|
@ -29,8 +29,11 @@ description: |
|
||||||
studysets published to Explore and copied, later releases coming in as
|
studysets published to Explore and copied, later releases coming in as
|
||||||
merges that keep your own edits.
|
merges that keep your own edits.
|
||||||
|
|
||||||
It starts with an anonymous account; signing in with GitHub later keeps
|
It starts with an anonymous account; signing in with GitHub or an email
|
||||||
everything. It connects to learn.halkyon.eu, the project's instance.
|
address later keeps everything. It connects to learn.halkyon.eu, the project's
|
||||||
|
instance, or to any other: everything an instance needs is in the repository,
|
||||||
|
to run your own. The app is AGPL-3.0 and the server SSPL-1.0, so all of its
|
||||||
|
code is public.
|
||||||
|
|
||||||
Questions and reports: support@halkyon.app
|
Questions and reports: support@halkyon.app
|
||||||
license: AGPL-3.0-or-later
|
license: AGPL-3.0-or-later
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,25 @@ AntiFeatures:
|
||||||
Name: Halkyon Learn
|
Name: Halkyon Learn
|
||||||
Summary: Learn with flashcards, studied offline and synced between your devices
|
Summary: Learn with flashcards, studied offline and synced between your devices
|
||||||
Description: |-
|
Description: |-
|
||||||
Halkyon Learn is a spaced-repetition learning app: studysets of flashcards with
|
Halkyon Learn is a spaced-repetition app for your own material: studysets of
|
||||||
rich text, notes, learning paths and PDFs, reviewed on an FSRS schedule. It works
|
flashcards, notes, learning paths and PDFs, reviewed on an FSRS schedule. Every
|
||||||
offline and syncs between your devices.
|
studyset is made by its learners: build yours for any subject, or copy one that
|
||||||
|
someone published. It works offline and syncs between your devices.
|
||||||
|
|
||||||
It starts with an anonymous account; signing in with GitHub later keeps
|
Learn from what you watch: play films and shows from your Jellyfin server,
|
||||||
everything. It connects to learn.halkyon.eu, the project's instance.
|
and every subtitle line is split into dictionary words, with readings above
|
||||||
|
and the translation below. Tap a word, and it is a flashcard.
|
||||||
|
|
||||||
|
Dictionaries for Japanese, Chinese, Korean, German, Greek and English; kanji,
|
||||||
|
hanzi and Hangul written by hand, each stroke checked; exam plans for the
|
||||||
|
JLPT, HSK and Korean levels, with a daily pace to your exam's date; and
|
||||||
|
studysets published to Explore and copied, later releases coming in as
|
||||||
|
merges that keep your own edits.
|
||||||
|
|
||||||
|
It starts with an anonymous account; signing in with GitHub or an email
|
||||||
|
address later keeps everything. It connects to learn.halkyon.eu, the project's
|
||||||
|
instance, or to any other: everything an instance needs is in the repository,
|
||||||
|
to run your own. The app is AGPL-3.0 and the server SSPL-1.0, so all of its
|
||||||
|
code is public.
|
||||||
|
|
||||||
|
Questions and reports: support@halkyon.app
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue