El árbol de decisión
Cinco preguntas que llevan cualquier cosa que quieras guardar a su sitio, y el cierre del nivel: lo que ya puedes decidir antes de conocer las plataformas.
Las cinco preguntas
El paso 5 es el patrón que resuelve todo lo que no cabe en el paso 4: una base de datos entera, un directorio de documentos, un archivo grande. No se meten en el llavero —que no está pensado para eso y tiene límites de tamaño— sino que se cifran, y lo que se guarda en el llavero son los pocos bytes de la clave.
Los destinos
Lo que ya puedes decidir sin conocer las plataformas
Esto es lo importante del cierre: cuatro decisiones de diseño no dependen de macOS, Windows ni Linux, y se pueden tomar hoy.
- Qué guardas y qué no. El paso 1 y el paso 2 son de tu aplicación, no del sistema.
- La forma de tus identificadores. Nivel 34.3: servicio estable, cuenta que identifica al sujeto, comprensible para el usuario.
- Que el código que toca secretos viva en el core y no en el frontend. Nivel 31.5: lo que no llega a la webview no se puede leer desde ahí. El frontend pide operaciones —«inicia sesión», «llama a la API»—, nunca el testigo.
- Y que el camino de «no está» sea un camino normal. Porque el usuario puede borrar la entrada, cambiar de máquina o arrancar sin almacén, y las tres cosas van a pasar.
Lo que sí depende de la plataforma es más estrecho de lo que parece: cuándo aparece un diálogo, qué rompe el acceso, y qué hacer si el almacén no existe. Ésos son los tres niveles siguientes.
// la forma que sale de las cuatro decisiones: el secreto no cruza el puente#[tauri::command]async fn iniciar_sesion(app: AppHandle, email: String, clave: String) -> Result<Perfil, Error> { let testigo = servicio::autenticar(&email, &clave).await?; secretos::guardar(&app, &email, &testigo)?; // se queda en el core Ok(testigo.perfil) // sale el perfil, no el testigo}#[tauri::command]async fn sesion_actual(app: AppHandle) -> Result<Option<Perfil>, Error> { // "no está" es un camino normal, no un error match secretos::leer(&app)? { Some(t) => Ok(Some(servicio::perfil(&t).await?)), None => Ok(None), }}