fix(privacy): what the app shows and adds keeps private studysets private (ADR 0019)
The reading of 22 Oct, on the device's side. Private cards never move into a readable studyset: the card browser asks to make it private first. Locked studysets are left out of the move sheets and the destinations words are added to. A studyset whose key isn't on this device says to sign in with the password here, not that it is lost, and can still be archived; creating one there says why it can't. The marker shows the lock for a private studyset published before, and a shield with a warning while something in it is still readable or unsealed, with Finish in its dialog. The library's offer covers every top-level studyset, Recently deleted and orphaned ones too, says what couldn't be sealed, and its answer follows the person in their settings (schema 11). The full-window editors carry the studyset they were opened from, and an account with keys adds no file where none is known (a new studyset's description takes pictures once it is made). A file no live row names is never uploaded, and deleting a row clears what it owed. Sealed rows are indexed by key and hash. A suggestion from a private copy uploads readable copies of its files for the author; accepting swaps whole `asset:` values only. Docs: ADR 0019's "The reading (22 Oct 2026)" with the decisions, what changed and the residual risks; "As built" and Learn's AGENTS.md brought up to date. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
88e79155bc
commit
46a47d0975
5 changed files with 625 additions and 73 deletions
|
|
@ -1180,6 +1180,7 @@
|
|||
"fileNotAnImage": "Diese Datei ist kein Bild, das die App anzeigen kann.",
|
||||
"fileNotASound": "Diese Datei ist keine Audiodatei, die die App abspielen kann.",
|
||||
"fileTooLarge": "Diese Datei ist größer als {megabytes} MB.",
|
||||
"fileAfterCreate": "Bilder und Töne lassen sich hinzufügen, sobald das Lernset erstellt ist.",
|
||||
"fileNotKept": "Die Datei konnte nicht auf diesem Gerät gespeichert werden.",
|
||||
"fileRemoveImage": "Bild entfernen",
|
||||
"fileRemoveSound": "Audio entfernen",
|
||||
|
|
@ -2072,9 +2073,18 @@
|
|||
"studysetReadableBody": "Alles darin wird entschlüsselt und neu synchronisiert, sodass die Instanz es lesen kann: Du kannst es veröffentlichen, und Claude kann es über den Connector lesen.",
|
||||
"studysetPrivateNoKeys": "Melde dich auf diesem Gerät mit deinem Passwort an, um Lernsets privat zu machen.",
|
||||
"studysetPrivateSomeLocked": "Manches darin ließ sich auf diesem Gerät nicht öffnen und blieb, wie es war.",
|
||||
"studysetPrivateLabel": "Privat",
|
||||
"studysetLockedTitle": "Gesperrt",
|
||||
"studysetLockedBody": "Dieses Lernset ist privat, und sein Schlüssel öffnet sich auf diesem Gerät nicht: Es wurde mit Schlüsseln verschlüsselt, die dein Konto nicht mehr hat. Es lässt sich nur noch löschen.",
|
||||
"studysetVisibilityPartly": "Privat, teils lesbar",
|
||||
"studysetPrivacyPartlyBody": "Manches darin kann die Instanz noch lesen: was ein Gerät schrieb, bevor es davon wusste, oder Dateien, die noch nicht umgezogen sind. Es wird versiegelt, sobald ein Gerät mit deinen Schlüsseln es kann, oder jetzt mit Abschließen.",
|
||||
"studysetPrivacyFinish": "Abschließen",
|
||||
"studysetLockedNoKeysBody": "Dieses Lernset ist privat, und sein Schlüssel ist nicht auf diesem Gerät. Melde dich auf diesem Gerät mit deinem Passwort an, um es zu öffnen.",
|
||||
"cardMovePrivateTitle": "„{title}“ privat machen?",
|
||||
"@cardMovePrivateTitle": {"placeholders": {"title": {"type": "String"}}},
|
||||
"cardMovePrivateBody": "Diese Karteikarten sind privat. „{title}“ kann die Instanz lesen, also können sie erst dorthin, wenn es auch privat ist.",
|
||||
"@cardMovePrivateBody": {"placeholders": {"title": {"type": "String"}}},
|
||||
"cardMovePrivateAction": "Privat machen und verschieben",
|
||||
"privacyKeysMissing": "Melde dich auf diesem Gerät mit deinem Passwort an: Was du hier hinzufügst oder änderst, wäre privat.",
|
||||
"libraryOfferPrivateTitle": "Deine Lernsets privat machen?",
|
||||
"libraryOfferPrivateBody": "Neue Lernsets sind privat: Ende-zu-Ende-verschlüsselt, sodass nur deine Geräte sie lesen können. Die, die du vorher gemacht hast, bleiben für die Instanz lesbar, bis du sie auch privat machst. Kopien in den Sicherungen der Instanz bleiben lesbar, bis diese ablaufen.",
|
||||
"libraryOfferPrivateAction": "Alle privat machen",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,7 @@
|
|||
"megabytes": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"fileAfterCreate": "Pictures and sounds can be added once the studyset is made.",
|
||||
"fileNotKept": "The file couldn't be kept on this device.",
|
||||
"fileRemoveImage": "Remove image",
|
||||
"fileRemoveSound": "Remove sound",
|
||||
|
|
@ -2438,9 +2439,18 @@
|
|||
"studysetReadableBody": "Everything in it is decrypted and synced again, so the instance can read it: you can publish it, and Claude can read it through the connector.",
|
||||
"studysetPrivateNoKeys": "Sign in with your password on this device to make studysets private.",
|
||||
"studysetPrivateSomeLocked": "Some of it couldn't be opened on this device and stayed as it was.",
|
||||
"studysetPrivateLabel": "Private",
|
||||
"studysetLockedTitle": "Locked",
|
||||
"studysetLockedBody": "This studyset is private, and its key doesn't open on this device: it was encrypted with keys your account no longer has. It can only be deleted.",
|
||||
"studysetVisibilityPartly": "Private, partly readable",
|
||||
"studysetPrivacyPartlyBody": "Some of it is still readable by the instance: what a device wrote before it heard, or files not moved yet. It is sealed as soon as a device with your keys can, or now with Finish.",
|
||||
"studysetPrivacyFinish": "Finish",
|
||||
"studysetLockedNoKeysBody": "This studyset is private, and its key isn't on this device. Sign in with your password on this device to open it.",
|
||||
"cardMovePrivateTitle": "Make “{title}” private?",
|
||||
"@cardMovePrivateTitle": {"placeholders": {"title": {"type": "String"}}},
|
||||
"cardMovePrivateBody": "These flashcards are private. “{title}” is readable by the instance, so they can only move there once it is private too.",
|
||||
"@cardMovePrivateBody": {"placeholders": {"title": {"type": "String"}}},
|
||||
"cardMovePrivateAction": "Make private and move",
|
||||
"privacyKeysMissing": "Sign in with your password on this device: what you add or change here would be private.",
|
||||
"libraryOfferPrivateTitle": "Make your studysets private?",
|
||||
"libraryOfferPrivateBody": "New studysets are private: end-to-end encrypted, so only your devices can read them. Those you made before stay readable by the instance until you make them private too. Copies in the instance's backups stay readable until those expire.",
|
||||
"libraryOfferPrivateAction": "Make all private",
|
||||
|
|
|
|||
|
|
@ -6946,6 +6946,12 @@ abstract class AppLocalizations {
|
|||
/// **'This file is larger than {megabytes} MB.'**
|
||||
String fileTooLarge(int megabytes);
|
||||
|
||||
/// No description provided for @fileAfterCreate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pictures and sounds can be added once the studyset is made.'**
|
||||
String get fileAfterCreate;
|
||||
|
||||
/// No description provided for @fileNotKept.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -12219,12 +12225,6 @@ abstract class AppLocalizations {
|
|||
/// **'Some of it couldn\'t be opened on this device and stayed as it was.'**
|
||||
String get studysetPrivateSomeLocked;
|
||||
|
||||
/// No description provided for @studysetPrivateLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Private'**
|
||||
String get studysetPrivateLabel;
|
||||
|
||||
/// No description provided for @studysetLockedTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -12237,6 +12237,54 @@ abstract class AppLocalizations {
|
|||
/// **'This studyset is private, and its key doesn\'t open on this device: it was encrypted with keys your account no longer has. It can only be deleted.'**
|
||||
String get studysetLockedBody;
|
||||
|
||||
/// No description provided for @studysetVisibilityPartly.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Private, partly readable'**
|
||||
String get studysetVisibilityPartly;
|
||||
|
||||
/// No description provided for @studysetPrivacyPartlyBody.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Some of it is still readable by the instance: what a device wrote before it heard, or files not moved yet. It is sealed as soon as a device with your keys can, or now with Finish.'**
|
||||
String get studysetPrivacyPartlyBody;
|
||||
|
||||
/// No description provided for @studysetPrivacyFinish.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Finish'**
|
||||
String get studysetPrivacyFinish;
|
||||
|
||||
/// No description provided for @studysetLockedNoKeysBody.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This studyset is private, and its key isn\'t on this device. Sign in with your password on this device to open it.'**
|
||||
String get studysetLockedNoKeysBody;
|
||||
|
||||
/// No description provided for @cardMovePrivateTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Make “{title}” private?'**
|
||||
String cardMovePrivateTitle(String title);
|
||||
|
||||
/// No description provided for @cardMovePrivateBody.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'These flashcards are private. “{title}” is readable by the instance, so they can only move there once it is private too.'**
|
||||
String cardMovePrivateBody(String title);
|
||||
|
||||
/// No description provided for @cardMovePrivateAction.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Make private and move'**
|
||||
String get cardMovePrivateAction;
|
||||
|
||||
/// No description provided for @privacyKeysMissing.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Sign in with your password on this device: what you add or change here would be private.'**
|
||||
String get privacyKeysMissing;
|
||||
|
||||
/// No description provided for @libraryOfferPrivateTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -4315,6 +4315,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
return 'Diese Datei ist größer als $megabytes MB.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get fileAfterCreate =>
|
||||
'Bilder und Töne lassen sich hinzufügen, sobald das Lernset erstellt ist.';
|
||||
|
||||
@override
|
||||
String get fileNotKept =>
|
||||
'Die Datei konnte nicht auf diesem Gerät gespeichert werden.';
|
||||
|
|
@ -7747,9 +7751,6 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get studysetPrivateSomeLocked =>
|
||||
'Manches darin ließ sich auf diesem Gerät nicht öffnen und blieb, wie es war.';
|
||||
|
||||
@override
|
||||
String get studysetPrivateLabel => 'Privat';
|
||||
|
||||
@override
|
||||
String get studysetLockedTitle => 'Gesperrt';
|
||||
|
||||
|
|
@ -7757,6 +7758,37 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get studysetLockedBody =>
|
||||
'Dieses Lernset ist privat, und sein Schlüssel öffnet sich auf diesem Gerät nicht: Es wurde mit Schlüsseln verschlüsselt, die dein Konto nicht mehr hat. Es lässt sich nur noch löschen.';
|
||||
|
||||
@override
|
||||
String get studysetVisibilityPartly => 'Privat, teils lesbar';
|
||||
|
||||
@override
|
||||
String get studysetPrivacyPartlyBody =>
|
||||
'Manches darin kann die Instanz noch lesen: was ein Gerät schrieb, bevor es davon wusste, oder Dateien, die noch nicht umgezogen sind. Es wird versiegelt, sobald ein Gerät mit deinen Schlüsseln es kann, oder jetzt mit Abschließen.';
|
||||
|
||||
@override
|
||||
String get studysetPrivacyFinish => 'Abschließen';
|
||||
|
||||
@override
|
||||
String get studysetLockedNoKeysBody =>
|
||||
'Dieses Lernset ist privat, und sein Schlüssel ist nicht auf diesem Gerät. Melde dich auf diesem Gerät mit deinem Passwort an, um es zu öffnen.';
|
||||
|
||||
@override
|
||||
String cardMovePrivateTitle(String title) {
|
||||
return '„$title“ privat machen?';
|
||||
}
|
||||
|
||||
@override
|
||||
String cardMovePrivateBody(String title) {
|
||||
return 'Diese Karteikarten sind privat. „$title“ kann die Instanz lesen, also können sie erst dorthin, wenn es auch privat ist.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get cardMovePrivateAction => 'Privat machen und verschieben';
|
||||
|
||||
@override
|
||||
String get privacyKeysMissing =>
|
||||
'Melde dich auf diesem Gerät mit deinem Passwort an: Was du hier hinzufügst oder änderst, wäre privat.';
|
||||
|
||||
@override
|
||||
String get libraryOfferPrivateTitle => 'Deine Lernsets privat machen?';
|
||||
|
||||
|
|
|
|||
|
|
@ -4277,6 +4277,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
return 'This file is larger than $megabytes MB.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get fileAfterCreate =>
|
||||
'Pictures and sounds can be added once the studyset is made.';
|
||||
|
||||
@override
|
||||
String get fileNotKept => 'The file couldn\'t be kept on this device.';
|
||||
|
||||
|
|
@ -7674,9 +7678,6 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get studysetPrivateSomeLocked =>
|
||||
'Some of it couldn\'t be opened on this device and stayed as it was.';
|
||||
|
||||
@override
|
||||
String get studysetPrivateLabel => 'Private';
|
||||
|
||||
@override
|
||||
String get studysetLockedTitle => 'Locked';
|
||||
|
||||
|
|
@ -7684,6 +7685,37 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get studysetLockedBody =>
|
||||
'This studyset is private, and its key doesn\'t open on this device: it was encrypted with keys your account no longer has. It can only be deleted.';
|
||||
|
||||
@override
|
||||
String get studysetVisibilityPartly => 'Private, partly readable';
|
||||
|
||||
@override
|
||||
String get studysetPrivacyPartlyBody =>
|
||||
'Some of it is still readable by the instance: what a device wrote before it heard, or files not moved yet. It is sealed as soon as a device with your keys can, or now with Finish.';
|
||||
|
||||
@override
|
||||
String get studysetPrivacyFinish => 'Finish';
|
||||
|
||||
@override
|
||||
String get studysetLockedNoKeysBody =>
|
||||
'This studyset is private, and its key isn\'t on this device. Sign in with your password on this device to open it.';
|
||||
|
||||
@override
|
||||
String cardMovePrivateTitle(String title) {
|
||||
return 'Make “$title” private?';
|
||||
}
|
||||
|
||||
@override
|
||||
String cardMovePrivateBody(String title) {
|
||||
return 'These flashcards are private. “$title” is readable by the instance, so they can only move there once it is private too.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get cardMovePrivateAction => 'Make private and move';
|
||||
|
||||
@override
|
||||
String get privacyKeysMissing =>
|
||||
'Sign in with your password on this device: what you add or change here would be private.';
|
||||
|
||||
@override
|
||||
String get libraryOfferPrivateTitle => 'Make your studysets private?';
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue