Commit graph halkyon/apps/meditate/halkyon_meditate_client/lib/migrations
Author SHA1 Message Date
b7f0aaaec7
refactor(base,learn): people's files kept by halkyon_base
ADR 0043, D2, D4, D6 and D7, the first step towards Todo's attachments:
Learn's file store moves into the module, for every server that keeps
files, and Learn becomes its first host.

halkyon_base gets the store as Learn had it: uploads into the caller's
own space by SHA-256, downloads by a row the caller may read, each
person's room (FILE_LIMIT_IDENTIFIED_MB, FILE_LIMIT_INSTANCE_MB, the
operator's file_allowance, which moves into the module unchanged) and
the pruning of files no live row names. It stays free of the sync
engine: a host hands it a FileStore saying which of its rows name a file
and what it takes (HalkyonBase.files); without one the files endpoint
refuses. Its tests, with refusals, run on a stand-in host.

Learn hands it learnFiles: its asset rows, read by their space's members,
a readable file over 20 MB only as a PDF, up to 100 MB. Its app calls the
module; its own asset endpoint stays for released apps alone, forwarding
to the module in Learn's own models, whose names are on the wire. Its
file tests still pass through it, end to end.

Migrations: the module's creates file_allowance. Learn's server migration
holds version rows alone, as tables are matched by name (ADR 0014); its
device migration creates the module's empty table, as every module's.
Todo's, Train's and Meditate's create the empty table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 01:19:28 +01:00
929974443d
feat(meditate): the operator's library on the server, and a course's lessons done synced
An operator's folder (MEDITATE_LIBRARY_DIR) of recordings, pictures and a
YAML manifest, library.yaml (commented sample: library.example.yaml), read
as the server starts and every ten minutes, held in memory. Items without
a licence or credit, files outside the folder, unknown categories and
courses pointing at items not served are refused and logged; a manifest
that doesn't read keeps the library served before. bin/check_library.dart
checks a folder before deploying.

The catalogue goes to signed-in people alone (LibraryEndpoint), with a
12-hour access the server signs; the web server's /library/<served name>
serves the files to that access only, by ranges (relic's StaticHandler).
No request path reaches the file system.

lesson_done: a lesson done, sealed and synced, a row each; the migration
creates the table with its spaceId index and the cascading foreign key to
the sync engine's spaces, as dream's did. ADR 0038, D16.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:31:59 +01:00
769ae3b20a
feat(meditate): dreams on the server, sealed and synced like the sittings
The dream journal's table (ADR 0038, D14): `dream`, in the sync engine's
database, a row a dream, its content sealed on the device (key ID,
payload, nonce), so the instance sees only that a dream exists, its size
and when it changed. The same rules as the sittings: the sync engine's
connector refuses a call without a session, each row lives in its
person's space, and deleting the person deletes their dreams with it
(the deletion test now writes and counts a dream too, and leaves
another person's).

The migration `20261103130213716-dreams`:
- `CREATE OR REPLACE FUNCTION gen_random_uuid_v7()`: the generator
  repeats the UUID v7 function every migration; replacing it with the
  same body changes nothing.
- `CREATE TABLE "dream"`: `id` a UUID v7 by default, `spaceId` the
  person's sync space, `keyId` the key it is sealed with, `payload` the
  sealed bytes (not null), `nonce` the sealing's nonce.
- `CREATE INDEX "dream_space_idx"` on `spaceId`, so deleting a space
  finds its dreams without scanning the table.
- The foreign key from `spaceId` to `offline_sync_spaces("id")` with
  `ON DELETE CASCADE`: deleting the space deletes its dreams.
- The version rows of every module in `serverpod_migrations`.
A new, empty table: nothing existing changes and no data moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:31:57 +01:00
235d9cf75b
feat(meditate): Meditate's server, its sealed tables, and SimpleLogin's way back to it
halkyon_meditate_server and its generated client, copied from Todo's
and stripped to the module, the register and the sync engine (ADR 0038,
D1, D7): content_key, meditate_settings and sitting, synced and sealed,
each indexing spaceId and deleted with the person's space; two
migrations, the initial one and sittings (purely additive, the ADR
explains the SQL); ports 8280 to 8282 and PostgreSQL 8290 (D13).
Tests: the module's basics naming `meditate`, the sync engine refusing
calls without a session, the register's modes, and a person's deletion
taking their rows alone.

halkyon_base's forwarding page knows Meditate's ways back
(meditate-loopback on 43826, meditate-scheme app.halkyon.meditate),
which halkyon_app's repository names among the apps' ports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:31:56 +01:00