From ee2fdc1e93862aebde1524186d188fc68ec04286 Mon Sep 17 00:00:00 2001 From: vjrj Date: Sat, 11 Jul 2026 13:17:32 +0200 Subject: [PATCH] docs(trust): record the ego-centric pivot and ratings v1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit network-trust.md §2 rewritten (ego-centric model, misuse resistance, honest losses, unburnt Duniter bridge) + new §2.1 for ratings. open-decisions.md: 2026-07-10 WoT-parameters decision superseded; two new dated entries (ego-centric trust, ratings v1 with its soft conversation anchor and the strong-anchor question kept open). CLAUDE.md Block 2 paragraph updated to match. --- CLAUDE.md | 4 ++-- docs/design/network-trust.md | 24 +++++++++++++++++------- docs/design/open-decisions.md | 7 ++++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index fb2d0fb..9522395 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,9 +63,9 @@ Decided schema details: `Quantity` is a shared value type (Lot + Movement); `off ## Phasing — build order **Block 1: Inventory.** Shippable alone, useful with zero network. Delivered (beta). -**Block 2 (STARTED — the social leap):** offers + messaging + relays + web of trust — big and indivisible. The de-risking spike ([`docs/design/spike-block2-findings.md`](docs/design/spike-block2-findings.md)) validated the whole happy path, so the **social round is now open**. Build order within Block 2: **(1) the transport foundation in `commons_core`** — one `NostrConnection` + three interfaces (`OfferTransport`/`MessageTransport`/`TrustTransport`) + the pure `WebOfTrust` rule, on vetted libraries — then (2) offers UI, (3) messaging hardening, (4) WoT parameters + cold-start. It is still large: scope honestly, keep it behind the transport interfaces, and don't let it regress Block 1. +**Block 2 (STARTED — the social leap):** offers + messaging + relays + trust — big and indivisible. The de-risking spike ([`docs/design/spike-block2-findings.md`](docs/design/spike-block2-findings.md)) validated the whole happy path, so the **social round is now open**. Build order within Block 2: **(1) the transport foundation in `commons_core`** — one `NostrConnection` + N interfaces (`OfferTransport`/`MessageTransport`/`TrustTransport`/`RatingTransport`) + the pure `WebOfTrust` engine, on vetted libraries — then (2) offers UI, (3) messaging hardening, (4) trust & reputation polish. It is still large: scope honestly, keep it behind the transport interfaces, and don't let it regress Block 1. -Social layer uses: Nostr (offers NIP-99, DMs NIP-17, via the pure-Dart `nostr` package), Duniter-style **web of trust** (Duniter/Ğ1 compatible, ~5 certifications), optional **Ğ1** currency (levels: price → wallet deep-link → optional WoT import). Identity: a **secp256k1 Nostr key derived (HKDF, domain-separated, one-way) from the Ğ1 root seed** — user backs up ONE seed. Cold-start via real Ğ1 seed groups + fairs. See [`docs/design/network-trust.md`](docs/design/network-trust.md), [`docs/design/g1-integration.md`](docs/design/g1-integration.md), and the spike findings above. +Social layer uses: Nostr (offers NIP-99, DMs NIP-17, via the pure-Dart `nostr` package), **ego-centric trust** (you vouch for people you've met — kind 30777, one live cert per pair, expiring; your circle = friends-of-friends from YOUR key; **no global membership, no bootstrap referents, no user-facing parameters** — the global Duniter rule was dropped 2026-07-11 as not internationally viable, see open-decisions.md), **Wallapop-style ratings** (kind 30778, one live rating per pair, circle-weighted display), optional **Ğ1** currency (levels: price → wallet deep-link → optional WoT import remains a possible future enrichment). Identity: a **secp256k1 Nostr key derived (HKDF, domain-separated, one-way) from the Ğ1 root seed** — user backs up ONE seed. Cold-start via real Ğ1 seed groups + fairs. See [`docs/design/network-trust.md`](docs/design/network-trust.md), [`docs/design/g1-integration.md`](docs/design/g1-integration.md), and the spike findings above. ## Where things live diff --git a/docs/design/network-trust.md b/docs/design/network-trust.md index 25efda6..83f49df 100644 --- a/docs/design/network-trust.md +++ b/docs/design/network-trust.md @@ -13,16 +13,26 @@ Una app de compartición local no sirve si eres la única persona en 50 km. Dos **Primer campo de prueba: los grupos de semillas de Ğ1.** En la comunidad Ğ1 ya hay grupos de semillas, pero les falla la app y la red para intercambiar. Son el **piloto ideal**: comunidad motivada, afín, **ya en red y con confianza (WoT) y moneda (Ğ1) montadas** — solo les falta la herramienta. Para ese subconjunto, el arranque en frío está casi resuelto. Detalle en [g1-integration.md](g1-integration.md). -## 2. Red de confianza estilo Duniter (huecos 3 y 4) +## 2. Confianza egocéntrica (huecos 3 y 4) -Modelo inspirado en **Duniter / Ğ1**: cualquiera participa desde el minuto uno, pero empieza como **"desconocido"**. Cuando **N personas** que ya son miembros te **certifican** (una firma: "doy fe de esta persona"), entras en el área de **"gente conocida"** (miembro de la red de confianza). Duniter usa ~5 certificaciones + una regla de distancia; el umbral es un **parámetro ajustable**. +> **Revisión 2026-07-11.** La primera versión de esta sección proponía la regla *global* de membresía de Duniter (N certificaciones + distancia desde referentes fundadores curados). Se descartó: esa regla resuelve identidad sybil-proof para una renta básica — un problema que Tane no tiene — y exige curar referentes por comunidad/país, inviable en una app internacional (¿quién cura los fundadores de Japón, Brasil, Marruecos?). Un set de referentes malo es peor que ninguno. Ver `open-decisions.md`. -- **Certificar = acto presencial y consciente** ("te conocí, respondo por ti"). Encaja con §1: las ferias generan certificaciones. -- **Resuelve el arranque de confianza (hueco 3):** al recién llegado no se le bloquea, solo se le marca como desconocido hasta acumular avales. -- **Resuelve la moderación (hueco 4):** spammers y estafadores se quedan en "desconocido" y se filtran o despriorizan solos; la red de confianza pone en cuarentena **sin autoridad central**. Encima se suma la **reputación** (valoraciones tras un trato cerrado). -- **UI para 10–80:** mostrar "conocido / desconocido" con lenguaje humano ("aún nadie de tu confianza responde por esta persona"), nunca jerga de grafos. +**Modelo egocéntrico:** los avales ("doy fe de esta persona") son públicos, firmados y compartidos — todo el mundo ve el mismo grafo — pero **no hay veredicto global**: cada persona calcula la confianza **desde su propia posición**. Es el modelo que funcionó en la práctica en PGP (la validez se calcula desde TU clave) y el que usa el ecosistema Nostr ("seguido por gente que sigues"). -*A decidir:* umbral de certificaciones (¿5?), regla de distancia, caducidad de certificaciones, y si un banco/colectivo puede certificar como entidad. +- **Certificar = acto presencial y consciente** ("te conocí, respondo por ti"). Encaja con §1: las ferias generan avales. En la UI es un toque: "Conozco a esta persona", en el chat. +- **Tu círculo:** tú, la gente que avalas, y la gente que ellos avalan (distancia ≤2). El chat lo muestra en humano: "En tu círculo" / "Avalada por N" / "Nadie la avala aún". +- **Cold-start resuelto de raíz (hueco 3):** valor desde el primer aval, día uno, en cualquier país. Un colectivo arranca su propio racimo sin pedir permiso ni esperar referentes. Al recién llegado no se le bloquea, solo se le marca como desconocido. +- **Moderación sin autoridad central (hueco 4):** un atacante puede crear mil claves que se avalen entre sí, pero ese racimo queda desconectado de *tu* vista — siguen siendo desconocidos para ti. Para colarse en tu círculo necesita un aval presencial de alguien de tu entorno (la escasez de "aristas de ataque" protege por observador, como en la investigación anti-sybil por grafo social). Los avales son públicos: avalar a un estafador tiene coste social. Caducan (365 días) y se renuevan, podando confianza rancia. +- **Reputación encima:** valoraciones tipo Wallapop (§2.1). +- **UI para 10–80:** "Tu gente" (a quién avalas / quién te avala), lenguaje humano, nunca jerga de grafos ni parámetros. + +**Qué se pierde honestamente:** el badge global "miembro de la red" y la garantía una-persona-una-identidad — herencia del problema de Duniter (RBU), no del nuestro. **Puerta no quemada:** el primitivo de certificación sigue siendo estructuralmente compatible con Duniter (una certificación viva por par, con caducidad); una capa futura de "confianza por comunidad" (una red de semillas define sus propias raíces y su propia regla) o una importación opcional de la WoT de Ğ1 pueden construirse encima del mismo motor sin migración. + +### 2.1 Reputación: valoraciones tipo Wallapop + +Los avales no cubren la estafa desde dentro del círculo; las valoraciones sí. **V1 (implementada):** estrellas 1–5 + comentario corto sobre una persona, públicas y firmadas; **una valoración viva por par** (re-valorar edita, no acumula — un mismo autor no puede amontonar reseñas); retirable. Anclaje blando: solo puedes valorar a alguien con quien tienes conversación. Se muestran en el detalle de oferta y en el chat, **ponderadas por tu círculo** ("N de gente que conoces") — las reseñas de desconocidos no se destacan, así que inflarlas con claves falsas no sirve de nada. **Evolución prevista:** anclaje fuerte a un trato cerrado con el formulario bilateral firmado (ver `sharing-model.md` §6). + +*A decidir:* si un banco/colectivo puede certificar como entidad (ver sharing-model §6). ## 3. Relays y descubrimiento: ¿la app como relay? (hueco 5) diff --git a/docs/design/open-decisions.md b/docs/design/open-decisions.md index 6ac6fc8..8c57c2e 100644 --- a/docs/design/open-decisions.md +++ b/docs/design/open-decisions.md @@ -21,10 +21,11 @@ Estas fijan `schemaVersion = 1` y el arranque técnico: - **Transporte de la capa social → Nostr (confirmado).** No se renuncia a Nostr: **Duniter no tiene mensajería**, así que Nostr es la vía para mensajes y ofertas. Se mantiene "una sola identidad" derivando la clave **secp256k1** (Nostr) de la semilla raíz Ğ1. Falta: confirmar madurez de los NIPs y la ruta de derivación. (Datapods de Duniter descartados: no están en servicio.) → [g1-integration.md](g1-integration.md) - **Estrategia de relays:** app-as-relay oportunista + proximidad física + relays comunitarios. → network-trust §3 -- **Parámetros de la red de confianza — RESUELTO/IMPLEMENTADO (2026-07-10).** Modelo **membresía Duniter completa**: regla pura `WebOfTrust.membersWith(seeds, WotParams)` con `WotParams` (sigQty/stepMax/sigValidity) por defecto Ğ1 (5/5/1año) **configurables** (`WotSettings`, keystore) — una red joven los afloja y aprieta al crecer. Cold-start honesto (sin inventar identidades): referentes "semilla" desde un asset empaquetado (vacío hasta curar fundadores reales) ∪ referentes que el usuario añade por npub/QR (`TrustReferents`). Caducidad ya la ponía el transporte (`certify` con `expiration`). UI: badge por `TrustTier` (miembro de la red > en tu círculo > avalado > desconocido) en el chat, y pantalla "Red de confianza" (gestión de raíces + parámetros) desde el perfil. Certificación por colectivo queda para sharing-model §6. → network-trust §2 +- **Confianza egocéntrica sustituye a la WoT global — DECIDIDO/IMPLEMENTADO (2026-07-11), SUPERSEDE la decisión del 2026-07-10.** La membresía Duniter completa (referentes curados + `WotParams` sigQty/stepMax, pantalla de raíces npub y steppers) se retiró de la app: resolvía identidad sybil-proof para una RBU — problema que Tane no tiene —, exigía curar referentes por comunidad/país (inviable internacionalmente; un set de referentes malo es peor que ninguno) y su pantalla violaba "palabras humanas, nunca jerga". **Queda solo la vista egocéntrica**: tú avalas ("Conozco a esta persona", en el chat) / tu círculo (distancia ≤2 desde ti, umbral 1) / avalado por N / desconocido. Cero bootstrap, cero parámetros de usuario; los avales caducan (365 días) y se renuevan. Un racimo sybil queda desconectado de la vista de cada observador — el filtro antispam se conserva sin veredicto global. **Se mantiene sin cambios** el primitivo (`Certification`, kind Nostr 30777, una cert viva por par) y el motor puro `WebOfTrust` en `commons_core` (usado para el círculo; capacidad latente para una futura "confianza por comunidad" o importación opcional de la WoT Ğ1 — sin migración de eventos). **UI:** badge por `TrustTier` (en tu círculo > avalado > desconocido) en el chat; pantalla "Tu gente" (a quién avalas —revocable— / quién te avala) desde el perfil, sustituyendo a "Red de confianza". Eliminados `TrustReferents`, `WotSettings`, `TrustNetworkScreen` y el asset de referentes. Certificación por colectivo sigue en sharing-model §6. → network-trust §2 +- **Valoraciones tipo Wallapop v1 — DECIDIDO/IMPLEMENTADO (2026-07-11).** Estrellas 1–5 + comentario corto, públicas y firmadas (`Rating`, kind Nostr **30778** direccionable, `d` = sujeto): **una valoración viva por par** — re-valorar edita, no acumula; retirable. **Anclaje blando v1:** solo puedes valorar a alguien con quien tienes conversación (el anclaje fuerte a trato cerrado firmado sigue abierto, ver "Reputación" abajo). Se muestran en el detalle de oferta y en el chat **ponderadas por tu círculo** ("N de gente que conoces", mismo cálculo egocéntrico) — inflar reseñas con claves desconocidas no destaca nada; sin valoraciones no se muestra nada (la ausencia nunca es un reproche). Quinto transporte (`RatingTransport`) sobre el canal compartido. → network-trust §2.1 - **Mensajería:** alcance v1 (1:1 atada a oferta), apoyo en NIP-17. → network-trust §4 - - **Almacenamiento del historial de chat — RESUELTO/IMPLEMENTADO (2026-07-11).** El historial 1:1 se guarda en una **BD Drift/SQLCipher aparte** (`tane_chat.sqlite` / `ChatDatabase`), no en el keystore (era un blob JSON por conversación con read-modify-write O(n²), tope silencioso de 200 e índice manual). BD separada a propósito: el chat es **caché de red efímera por dispositivo**, no inventario — sin metadatos CRDT, fuera de la serie de migraciones del inventario, fuera del sync/backup. `append` = un insert indexado (dedup por clave única), historial sin tope. Pre-release: no se migran los blobs viejos del keystore. Mismo antipatrón (a escala trivial) en `OfferOutbox`/`TrustReferents`/`SocialSettings` → keystore aceptable ahí; solo el chat crece con el uso. → [chat-storage.md](chat-storage.md) -- **Reputación** atada a un trato/oferta cerrada (evitar reseñas falsas). → sharing-model §6 + - **Almacenamiento del historial de chat — RESUELTO/IMPLEMENTADO (2026-07-11).** El historial 1:1 se guarda en una **BD Drift/SQLCipher aparte** (`tane_chat.sqlite` / `ChatDatabase`), no en el keystore (era un blob JSON por conversación con read-modify-write O(n²), tope silencioso de 200 e índice manual). BD separada a propósito: el chat es **caché de red efímera por dispositivo**, no inventario — sin metadatos CRDT, fuera de la serie de migraciones del inventario, fuera del sync/backup. `append` = un insert indexado (dedup por clave única), historial sin tope. Pre-release: no se migran los blobs viejos del keystore. Mismo antipatrón (a escala trivial) en `OfferOutbox`/`SocialSettings` → keystore aceptable ahí; solo el chat crece con el uso. → [chat-storage.md](chat-storage.md) +- **Reputación: anclaje fuerte** a un trato/oferta cerrada (evitar reseñas falsas del todo) — la v1 con anclaje blando por conversación ya está (ver arriba); el anclaje fuerte espera al formulario bilateral firmado. → sharing-model §6 - **Precio:** ¿monedas comunitarias / de tiempo además de dinero? → sharing-model §6 - **Integración Ğ1 (moneda libre):** niveles 1–2 (precio en Ğ1 + enlace a cartera Ğecko/Cesium²/Ğ1nkgo) en la capa social; nivel 3 (reusar la WoT de Ğ1 como fuente de confianza) como estudio aparte. → [g1-integration.md](g1-integration.md) - **Ofertas de banco colectivo** (publica la entidad, no la persona). → sharing-model §6