tane/docs/design/open-decisions.md
vjrj 993f7b37ab feat(identity): switch social identity, all from the one backup
Adds pseudonymous, switchable social identities derived from the SAME root
seed via an account index (NostrKeyDerivation.deriveFromSeed(seed, account)).
HKDF is one-way so accounts are unlinkable to the Ğ1 key; account 0 is the
original identity, byte-for-byte unchanged (no rotation for current users),
and every account regenerates from the single seed — so switching adds
nothing to back up.

- SocialAccountStore: keystore-backed active account + max created.
- Per-identity stores (chats, profile, name cache) namespaced by account
  scope (account 0 = legacy keys, no migration) so identities never mix.
- switchSocialAccount() re-derives the identity, re-scopes the stores and
  restarts the inbox listener; RestartWidget rebuilds the tree to pick up
  the new social singletons. DB/inventory untouched.
- Profile 'Your identities' switcher: list, create, switch (with a note
  that messages/contacts are kept separate per identity). i18n en/es/pt/ast.

Tests: account-indexed derivation (legacy 0 unchanged, accounts distinct
yet deterministic, negatives rejected); SocialAccountStore; per-identity
store scope isolation. Resolves the flagged 'change identity' decision
(open-decisions §B).

Known: switching resets navigation to home (full tree rebuild).
2026-07-10 20:22:24 +02:00

61 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Tanemaki — Decisiones abiertas y "qué chirría"
*Revisión de conjunto antes de seguir construyendo. Reúne los cabos sueltos repartidos por los docs de diseño, ordenados por cuándo hay que decidirlos, y nombra las tensiones del plan que conviene mirar de frente.*
> **Actualización (2026-07-10) — se abre la ronda social (Block 2).** El spike de de-risking ([spike-block2-findings.md](spike-block2-findings.md)) validó el happy-path completo de la capa social: derivación de la clave Nostr desde la semilla Ğ1, `OfferTransport`/NIP-99, mensajería NIP-17 (privada y con metadatos ocultos) y red de confianza estilo Duniter, todo sobre **una `NostrConnection` compartida con tres interfaces**. Con eso, la puerta de "no empezar Block 2" queda **levantada**. Orden de construcción dentro de Block 2: **(1) base de transporte en `commons_core`** (sobre librerías contrastadas — `nostr`, Dart puro, LGPL) → (2) UI de ofertas → (3) endurecer mensajería (entrega offline, vectores NIP-44, multidispositivo) → (4) parámetros WoT + arranque en frío. Los ítems de §B siguen siendo las decisiones a cerrar; §D.3 y §D.7 quedan **resueltos** por el spike.
## A) Decidir antes de escribir código de la Fase 1 (inventario)
Estas fijan `schemaVersion = 1` y el arranque técnico:
- **Formato de exportación/copia → ✅** un **archivo único cifrado**, abierto, documentado y versionado (volcado SQLite/CRDT + fotos + semilla envuelta). → [backup-and-recovery.md](backup-and-recovery.md)
- **Cifrado en reposo (el "cómo" técnico):** SQLCipher + llave aleatoria en el almacén del sistema + QR de recuperación. Conceptualmente decidido; falta bajar el mecanismo. → [security-privacy.md](security-privacy.md)
- **Identidad → ✅ una sola clave** raíz (Duniter/Ğ1), de la que se deriva la secp256k1 para Nostr. La clave simétrica que cifra la BD en reposo es un **detalle interno aparte** (no es una identidad), así que no contradice el "una sola identidad". → [g1-integration.md](g1-integration.md), [security-privacy.md](security-privacy.md)
- **Detalles de modelo que fijan el esquema → ✅** `Quantity` como **tipo compartido** (Lot y Movement); `offer_status` **solo en Lot**; `category` **texto libre** prerrellenado desde `Species.family`. → [data-model.md](data-model.md) §6
- **Forma del lote (`LotType`) y envase (`Presentation`) → ✅** (schema v4). `LotType` pasa de `{seed, plant}` a **6 formas** append-only: `seed, seedling(plantón), plant, tree, bulb, cutting` — tras repasar el vocabulario de viveros/tiendas ("plantel" era mal nombre: es el semillero, no una unidad). El **envase** (`pot/tray/plug/bareRoot/rootBall`) es un **atributo opcional aparte**, no una cantidad, para no mezclar "cuántas" con "en qué formato". Germinación sigue siendo solo-`seed`. → [data-model.md](data-model.md) §2.3.2
- **Licencia** del código → ✅ **AGPL-3.0** (GPLv3 + cláusula de red; compatible con GPLv3 y con Duniter/Ğ1nkgo). Falta añadir el fichero `LICENSE` al abrir el repo.
- **Varilla v1 → ✅ viable** con datos abiertos: nombres/identificación (Wikidata+GBIF, fácil), **conservación desde Kew SID** (dominio público, ~52k taxones), cuidados enlazados de Permapeople. Falta: confirmar acceso/descarga de Kew SID y el subconjunto curado a empaquetar. → [data-notes.md](data-notes.md) §3-ter
- **Estructura del workspace** (`commons_core` + `app_seeds`, pub workspaces) — decidido; falta materializarlo.
## B) Decidir antes de la capa de compartir (Fase 3) — muy interdependientes
- **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:** umbral de certificaciones (¿5, estilo Duniter?), distancia, caducidad, certificación por colectivo. → network-trust §2
- **Mensajería:** alcance v1 (1:1 atada a oferta), apoyo en NIP-17. → network-trust §4
- **Reputación** atada a un trato/oferta cerrada (evitar reseñas falsas). → sharing-model §6
- **Precio:** ¿monedas comunitarias / de tiempo además de dinero? → sharing-model §6
- **Integración Ğ1 (moneda libre):** niveles 12 (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
- **Caducidad/revocación** de ofertas ya replicadas en relays. → sharing-model §6
- **Cambiar de identidad social — RESUELTO/IMPLEMENTADO (2026-07-10).** Se eligió una refinación de la vía (A) que **mantiene "backup UNA semilla"**: identidades seudónimas **derivadas de la misma semilla raíz con un índice de cuenta** (`NostrKeyDerivation.deriveFromSeed(seed, {account})`, HKDF one-way → no vinculables a Ğ1; **cuenta 0 = la identidad actual, byte-for-byte, sin rotar a nadie**). La cuenta activa se persiste en el keystore (`SocialAccountStore`), los stores por-identidad (chats/perfil/nombres) se **namespacian por cuenta** (cuenta 0 = claves legacy, sin migración), y `switchSocialAccount()` re-registra la porción social del DI y reinicia el inbox; la UI (perfil → "Tus identidades", crear/cambiar) aplica el cambio con un `RestartWidget` que reconstruye el árbol leyendo los singletons nuevos. La app NO toca la DB/inventario. Descartada la vía (B) reset total (perdía identidad+recuperación). → [[project-block2-spike]]
## C) Puede esperar (no bloquea nada ahora)
- Negación plausible / bóveda señuelo; modo discreto. → security-privacy
- Reconocimiento por foto en la varilla.
- Respaldo social distribuido entre contactos.
- Ampliar avisos legales más allá de Europa (empezar por jurisdicción común europea). → PLAN §6
- Segundo producto (biblioteca de cosas) y extracción de `Item/Holding` al core. → core-domain-boundary
- Bloqueo de app: ¿activado por defecto o sugerido? → security-privacy
- **Puente opcional con portales abiertos de biodiversidad (Darwin Core / GBIF / ALA).** Export *opt-in* de agrobiodiversidad (variedades cultivadas, taxonomía folk, nombres vernáculos) como Darwin Core Archive hacia un atlas ALA/GBIF — justo los datos que esos portales tienen más flojos. **No es fase ni código ahora:** solo se apunta como intención. Lo único que exige del diseño actual ya lo hacemos por otros motivos: anclar especies a GBIF key + Wikidata QID (data-model §2.2) y mantener el formato de exportación abierto/versionado (requisito de backup, Fase 1). **Guardas innegociables** para que no choque con el modelo de privacidad: opt-in por ítem, a nivel de banco colectivo (no persona), ubicación difuminada, solo material no sensible, y en un solo sentido (app → portal; nunca escribir de vuelta en un inventario privado). Argumento de interoperabilidad útil para NLnet/NGI. → data-notes §3-bis
## D) Qué chirría (tensiones a mirar de frente)
1. **La capa social es indivisible y grande.** "MVP = inventario + compartición local" subestima que "compartir" de verdad = **ofertas + mensajería + relays + red de confianza**, todo junto e interdependiente. Propuesta: replantear las fases como **(1) inventario** —entregable, sólido, en solitario— y **(2) el salto social** —un bloque grande, no un incremento—. Ser honestos con esto en el plan y en la financiación.
2. **Casi toda la dificultad vive en `commons_core`.** Identidad, sync CRDT, ofertas, pledge, red de confianza, **mensajería**, relays… `app_seeds` es, en comparación, ligero. En el fondo Tanemaki es **infraestructura de procomún** con las semillas como primera aplicación. Es buena noticia para NLnet/NGI Zero (financian infraestructura), pero hay que asumir que lo "sencillo" (semillas) es la punta del iceberg y comunicar el proyecto en consecuencia.
3. **Apuesta fuerte por Nostr.** Ofertas, mensajes y confianza dependerían de NIPs en evolución (99 / 17 / 85). La abstracción `OfferTransport` amortigua las ofertas, pero mensajería y confianza también se apoyan en Nostr → más acoplamiento del que sugería el plan. Revisar si un solo protocolo lo cubre bien o si conviene aislar también mensajería y confianza tras interfaces.
4. **Cifrado en reposo + sync CRDT + multidispositivo.** Hay que asegurar que la sincronización **no filtra la llave ni datos en claro**, y que la llave (QR) llega bien al segundo dispositivo. Encaja, pero es un punto técnico fino que conviene prototipar pronto.
5. **La "varilla" y los datos de conservación** necesitan una fuente y una curación que **aún no tenemos**. Puede ser más trabajo del que parece (y es, a la vez, de lo que más valor humano aporta).
6. **Alcance vs. sostenibilidad de un mantenedor.** El conjunto (inventario + cripto + P2P + confianza + mensajería + relays, en varias plataformas) es **mucho** para "proyecto personal + IA". Refuerza la necesidad de: fases honestas, comunidad temprana, y financiación (NLnet) para la parte social. La Fase 1 sí es abordable en solitario.
7. **Identidad — RESUELTO: una sola identidad, dos claves derivadas.** Una **semilla raíz** estilo Duniter/Ğ1 (perfil, certificaciones, Ğ1) de la que se **deriva** determinísticamente la clave **secp256k1** para Nostr (mensajes, ofertas). Para la persona es *una sola identidad y una sola copia* (el QR de la semilla). **Nostr no se descarta** — es imprescindible porque **Duniter carece de mensajería**. No hay contradicción con "una sola clave": son subclaves de una raíz. → [g1-integration.md](g1-integration.md)
## Recomendación de orden
Cerrar el bloque **(A)** — son pocas decisiones y desbloquean construir el inventario, que es valioso por sí solo y de bajo riesgo. Dejar **(B)** para una ronda de diseño específica de la capa social (con su propia financiación si llega). Y asumir explícitamente el punto **(D.1)**: replantear las fases para que "lo social" sea un hito grande y no una coletilla del MVP.