import 'dart:convert'; import 'package:commons_core/commons_core.dart'; import 'package:flutter/services.dart' show rootBundle; import '../security/secret_store.dart'; /// The web-of-trust bootstrap referents (Duniter "seeds"): identities every /// membership calculation trusts by construction. The network's roots. /// /// Sources, unioned: a bundled asset (`assets/trust/referents.json`, a curated /// founding set — empty until real founders are added) plus referents the user /// adds in-app (by npub / QR), kept in the keystore. All keys are stored and /// returned as 64-char hex. Solving cold-start honestly: no invented identities /// ship, and a user can bootstrap their own roots. class TrustReferents { TrustReferents(this._store, {this.assetPath = 'assets/trust/referents.json'}); final SecretStore _store; final String assetPath; static const _userKey = 'tane.social.trust.referents'; List? _bundledCache; /// Every referent (bundled ∪ user-added), as hex public keys. Future> all() async => {...await _bundled(), ...await userAdded()}; /// Referents the user added themselves (hex), for the management UI. Future> userAdded() async { final raw = await _store.read(_userKey); if (raw == null || raw.isEmpty) return {}; return raw.split('\n').where((s) => s.isNotEmpty).toSet(); } /// Adds a referent from an [npubOrHex]; returns its hex form. Idempotent. /// Throws [FormatException] if it is neither an npub nor a hex key. Future add(String npubOrHex) async { final hex = npubToHex(npubOrHex); // validates final current = await userAdded(); if (current.add(hex)) { await _store.write(_userKey, current.join('\n')); } return hex; } /// Removes a user-added referent (bundled ones can't be removed). Future remove(String hex) async { final current = await userAdded()..remove(hex); await _store.write(_userKey, current.join('\n')); } Future> _bundled() async { final cached = _bundledCache; if (cached != null) return cached; try { final decoded = jsonDecode(await rootBundle.loadString(assetPath)); final list = (decoded is Map ? decoded['referents'] : decoded) as List?; final out = []; for (final entry in list ?? const []) { try { out.add(npubToHex(entry as String)); } catch (_) { // Skip a malformed entry rather than break the whole set. } } return _bundledCache = out; } catch (_) { return _bundledCache = const []; // no/invalid asset → no bundled referents } } }