fix(privacy): privacy is a signed fact the instance can't take back (ADR 0019)
The reading of 22 Oct, against an instance that alters data. A data key's ID now says what it is for (a studyset's key naming it, the person's key, or a record that a studyset was made readable), and the ID is sealed with the key and signed by the account, so the instance can't relabel a key or make one up; the row's own columns are no longer trusted. A studyset is private while the latest signed record about it says so, and the device keeps the latest it saw in a ledger in the person's folder (privacy.json), 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. An item keeps the key it was last seen sealed with unless this device moves it or a signed record makes its tree readable. A readable row a device holding the keys writes carries the account's signature in place of a nonce; one nobody signed is locked wherever privacy holds (in private trees, for items once sealed, and for tags and settings once sealed), so the instance can't plant content, confirm a guessed tag name by folding, or switch Count me on. Signed readable rows from a device that hadn't heard are shown and sealed anew. Rewrites are kept as under way until done, write the top studyset last, hold writes into their tree meanwhile, and are finished as the data opens and after each merge; a sweep seals what a stale device wrote. Cards moved into a private tree are sealed with their files first; cards of a private tree are never moved into a readable one. A private topic moved out of its tree keeps its privacy with a key of its own. Settings carry a counter inside their sealed payload, and a lower one than seen is refused. The person's readable tags and settings are sealed once. A device without the keys of an account that has them writes nothing that would be private and reports nothing for the learner count; an anonymous person's keys kept but unreadable are never replaced. Publishing sends only the names of the tags the release carries. Key refusals are remembered per row. Tests on the real gateways over a local database: a replayed readable top row, a withheld key record, a moved parent, a planted revision, a signed stale revision, card and topic moves, an interrupted rewrite, a settings rollback, a device without keys, planted tags. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
8ea2f04384
commit
88e79155bc
1 changed files with 2622 additions and 444 deletions
|
|
@ -7,18 +7,24 @@ import '../../../support/memory_published_store.dart';
|
|||
/// The instance can't read the person's tags (ADR 0019, D2): publishing
|
||||
/// names them as this device reads them.
|
||||
void main() {
|
||||
test("publishing sends the tags' names this device reads", () async {
|
||||
test('publishing sends the names of the tags the release carries, as this '
|
||||
'device reads them, and no others', () async {
|
||||
final gateway = FakePublishGateway();
|
||||
final asked = <String>[];
|
||||
final publishing = PublishRepository(
|
||||
gateway,
|
||||
MemoryPublishedStore(),
|
||||
tagNames: () async => {'tag-1': 'JLPT-N5'},
|
||||
tagNames: (studysetId) async {
|
||||
asked.add(studysetId);
|
||||
return {'tag-1': 'JLPT-N5'};
|
||||
},
|
||||
);
|
||||
addTearDown(publishing.dispose);
|
||||
|
||||
await publishing.publish('studyset-1', slug: 'japanese', notes: '');
|
||||
|
||||
expect(gateway.lastTagNames, {'tag-1': 'JLPT-N5'});
|
||||
expect(asked, ['studyset-1']);
|
||||
});
|
||||
|
||||
test('without names to give, none are sent', () async {
|
||||
|
|
|
|||
Loading…
Reference in a new issue