eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1import Foundation
2
3// Private moderation preferences (#578): who *I've* muted, synced across my
4// devices as one blob sealed to my OWN public key — the exact posture the
5// nicknames blob uses (Nicknames.swift). Nothing changes for a muted person
6// and the server can't read the set; only this member's devices can.
7//
8// The JSON shape is the cross-platform contract — { v: 1, mutedPeople:
9// ["uid", …] }, exactly what the web seals (shell.js saveModPrefsNow). Match
10// it byte-for-name; drift here would silently fork the muted set per device.
11public enum ModPrefs {
12 private struct Blob: Codable {
13 var v: Int
14 // Optional on decode so a future blob that dropped/renamed the field
15 // reads as "nobody muted" instead of failing the whole open.
16 var mutedPeople: [String]?
17 }
18
19 /// Opens a sealed prefs blob with the member's PKCS#8 private key.
20 public static func open(blobB64: String, privPkcs8B64: String) throws -> Set<String> {
21 let plain = try ECIES.open(sealedB64: blobB64, privPkcs8B64: privPkcs8B64)
22 return Set((try JSONDecoder().decode(Blob.self, from: plain)).mutedPeople ?? [])
23 }
24
25 /// Seals a muted-people set to the member's own public key. Sorted so the
26 /// same set always seals the same plaintext (the ciphertext still varies —
27 /// fresh ephemeral key — but the contract stays deterministic).
28 public static func seal(_ muted: Set<String>, recipientPubB64: String) throws -> String {
29 let plain = try JSONEncoder().encode(Blob(v: 1, mutedPeople: muted.sorted()))
30 return try ECIES.seal(bytes: plain, recipientPubB64: recipientPubB64)
31 }
32}
33