feat(trust): full Duniter web-of-trust membership (params + referents)

Slice 4 of Block 2. The pure rule was already parameterised; this adds the
policy, cold-start and UI around it.

- commons_core: WotParams (sigQty/stepMax/sigValidity, Duniter defaults) +
  WebOfTrust.membersWith; npubToHex helper (NIP-19 decode) for adding roots.
- WotSettings (keystore): the active parameters, configurable — a young
  network loosens them and tightens as it grows, as Ğ1 did. Defaults Duniter.
- TrustReferents: the bootstrap 'seeds' membership is measured from — a
  bundled asset (empty until real founders are curated, no invented keys)
  unioned with referents the user adds by npub/QR. Honest cold-start.
- TrustCubit: computes the full membership verdict against referents+params
  alongside the personal circle, and exposes a TrustTier (networkMember >
  inYourCircle > vouched > unknown). Certifications issued with the active
  validity (they expire and renew, Duniter rule).
- UI: chat trust badge by tier; a 'Network of trust' screen (manage roots +
  advanced params) reached from the profile. i18n en/es/pt/ast.

Tests: WotParams/membersWith, npubToHex, TrustReferents, WotSettings, and
TrustCubit tiers/membership. Resolves the WoT-parameters decision
(open-decisions §B). Trust net stays empty/undetermined until seeded — by
design; users bootstrap their own roots.
This commit is contained in:
vjrj 2026-07-10 21:01:11 +02:00
parent f767ccd70e
commit e715b129f6
29 changed files with 1111 additions and 39 deletions

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import '../di/injector.dart' show switchSocialAccount;
import '../i18n/strings.g.dart';
@ -21,6 +22,7 @@ class ProfileScreen extends StatefulWidget {
required this.settings,
required this.profileStore,
required this.accounts,
this.trustNetworkEnabled = false,
super.key,
});
@ -29,6 +31,9 @@ class ProfileScreen extends StatefulWidget {
final ProfileStore profileStore;
final SocialAccountStore accounts;
/// Whether to offer the "Network of trust" entry (needs the WoT services).
final bool trustNetworkEnabled;
@override
State<ProfileScreen> createState() => _ProfileScreenState();
}
@ -207,6 +212,18 @@ class _ProfileScreenState extends State<ProfileScreen> {
onSwitch: (a) => _switchTo(a),
onNew: _newIdentity,
),
if (widget.trustNetworkEnabled) ...[
const SizedBox(height: 12),
const Divider(),
ListTile(
key: const Key('profile.trustNetwork'),
contentPadding: EdgeInsets.zero,
leading: const Icon(Icons.hub_outlined, color: seedGreen),
title: Text(t.wot.open),
trailing: const Icon(Icons.chevron_right, color: seedMuted),
onTap: () => context.push('/trust'),
),
],
],
),
);