docs(privacy): private studysets as built (ADR 0019)

Learn's AGENTS.md says how private studysets work (keys, the envelope,
locked rows, rewriting a tree) and what changed for the connector,
publishing, uploads and the learner count; ADR 0019 records what was built
otherwise than decided or not yet (a private studyset's files); the
halkyon_app README describes the content cipher and device keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-21 21:50:29 +02:00
commit dcdc9c1542
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -114,3 +114,29 @@ millisecond), its table and the columns it changed.
rule 6's way back is taken.
- Making a large library private takes a sync of everything in it.
- Nobody, the operator included, can restore a private studyset whose keys are lost.
## As built (21 Oct 2026)
- **Not yet:** a private studyset's files (D5) are still uploaded readable,
and an asset row's file name with them. The instance takes encrypted
uploads already (`startUpload`/`finishUpload`'s `encrypted`: no PDF check,
4 KiB more room); the app has yet to seal a file with its studyset's key
before hashing it, which every place adding a file (the editors, the PDF
import, copies, archives) must name the studyset for.
- **Count me** stays in the person's settings, which follow them to every
device, and the device mirrors it to the instance after each sync
(`counted_learner`), reporting the copies only while it is on: the
instance keeps the setting, as D2 asks, but the person's choice is still
made where it was.
- **Tag names:** the instance refuses a name sent for a tag that isn't the
person's and a release whose encrypted tag has none; names of the
person's tags the release doesn't carry are ignored rather than refused,
as the device sends all its tags' names.
- **Locked rows:** a studyset whose key doesn't open shows as locked; its
other rows read as blank, which the lists leave out; a tag or settings
row that doesn't open is left out entirely, so nothing folds or deletes
it on a device without the keys.
- **Identity binding:** each payload is sealed for its table, row id and
column, and signed by the account; the signature goes before the
ciphertext in the payload column.