docs(privacy): the reading of private studysets (ADR 0019)

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-22 09:29:56 +02:00
commit 28372a35e6
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -132,8 +132,10 @@ millisecond), its table and the columns it changed.
new rows in its content (whole `asset:` values and a PDF's file, never
text) and blanks the old rows nothing else names.
- **The marker** (the developer's, 21 Oct): on the library's cards and the
studyset's Share alike, a lock for private, an open lock for readable by
the instance and not published, a globe for published. The locks open
studyset's Share alike, a lock for private (published before or not), a
shield with a warning for private but partly readable or unsealed (the
reading of 22 Oct), an open lock for readable by the instance and not
published, a globe for published. The locks open
one dialog saying what that means and changing it (and, while not
private, leading to the Publish sheet); the globe opens the Publish
sheet.
@ -143,9 +145,8 @@ millisecond), its table and the columns it changed.
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.
person's and a release whose encrypted tag has none; since the reading of
22 Oct the device sends only the names of the tags the release carries.
- **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
@ -154,3 +155,89 @@ millisecond), its table and the columns it changed.
column, and signed by the account; the signature goes before the
ciphertext in the payload column.
## The reading (22 Oct 2026)
A second, independent reading found that the device decided what was
private, and which key to write with, from columns the instance controls
(the top studyset's `keyId`, `parentId`, `card.studysetId`), and trusted
every readable row; and that content stayed readable in ordinary use (cards
moved, pictures from the full-window editors, a rewrite cut short, a device
that hadn't heard yet). The developer decided:
- **R1** Private studysets hold against an instance that alters data, not
only one that reads it.
- **R2** Rollback is guarded for the settings alone (a counter inside their
sealed payload); other old sealed content handed back is integrity, not
confidentiality, and is accepted.
What changed:
- **Privacy is a signed fact.** A data key's ID says what it is for
(`s1.<studyset>.<ms>.<nonce>` a studyset's key, `p1.<ms>.<nonce>` the
person's, `r1.<studyset>.<ms>.<nonce>` a record that the studyset was
made readable, which ends its earlier keys); the ID is sealed with the
key and signed by the account, so the row's columns are never trusted. A
studyset is private while the latest signed record about it says so; the
device keeps the latest it saw (`privacy.json` in the person's folder,
never synced), so a record withheld or a row replayed changes nothing. A
tree's key is the nearest studyset's with such a record, itself or above,
so moving a private studyset under a readable one changes nothing.
- **Items keep their key.** The device keeps the key each item (a card, a
note, a path, a step, a PDF, a step's answers, a studyset, a file) was
last seen sealed with, and never writes it with a weaker one unless it
moves the item itself or a signed record makes its tree readable.
- **Readable rows are signed or distrusted.** A device holding the keys
signs every readable row it writes (the account's signature in place of
the nonce, for its table, row and column). Where privacy holds (a private
tree, an item once sealed, tags and settings once sealed), a readable row
nobody signed is locked: never shown, folded or adopted. A signed one,
from a device that hadn't heard yet, is shown and sealed anew.
- **Rewrites finish.** A rewrite is kept as under way (in the ledger) until
done, writes the top studyset last, and holds writes into its tree
meanwhile; as the data opens and after every sync that merged, unfinished
ones are finished and every private tree is swept for rows not under its
key. What stays unsealed (locked rows, files not had) shows on the marker
as partly readable, with Finish in its dialog.
- **Moves.** Cards moved into a private tree are sealed with their files
before they move; cards of a private tree are never moved into a
readable one: the app asks to make the target private first. A private
topic moved out of its tree (into a readable one, or to the top) keeps its
privacy with a key of its own (this changes Decision 1: privacy can now
be a topic's own).
- **Tags and settings** are sealed once with the personal key, all readable
ones then; from then on a readable one nobody signed is left out, so the
instance can't confirm a guessed name by having a sealed tag folded into
it. Settings carry a counter (`HLKS`, 8 bytes, then the payload, inside
the seal); one lower than seen is refused.
- **A device without the account's keys,** on an account that has them,
writes nothing that would be private (new studysets, tags, settings are
refused, and it says so), reports nothing for the learner count, and
shows private studysets as needing the password here, not as lost
(archiving stays). An anonymous person's keys kept but unreadable are
never replaced.
- **Files.** The full-window editors carry the studyset they were opened
from; where none is known, an account with keys adds no file (a new
studyset's description takes pictures once it is made). A file no live
row names is never uploaded. A suggestion from a private copy uploads
readable copies of its files for the author; accepting swaps whole
`asset:` values only. Old file rows are kept while a row this device
can't open, or a sent suggestion, might name them.
- **The instance:** an upload said to be encrypted must start as a sealed
file does (`HLKF`, version 1); a person's room counts the files a live
row names and those claimed within the week of grace; copy reports are
limited to 60 an hour; publishing gets only the release's tags' names.
Residual risks, written down:
- **A fresh device** that never saw a studyset private, while the instance
withholds its records and hands back an old readable row, can be made to
write readable there. Nothing on such a device says otherwise; the
person's other devices still see the signed records once they come.
- **First sealing of tags and settings** on each device trusts what is
readable then: rows the instance planted before become the person's.
- **Old sealed content handed back** (a title, a card's earlier revision)
shows as it was: integrity, not confidentiality (R2).
- **Structure and rhythm** stay readable as before (D7); a card's place in
a tree is the instance's to change, which the item's own key keeps from
mattering for what is written, not for where it shows.