**Motivations:** - Implement BIP39 mnemonic import for identity creation - Add password-based key protection for enhanced security - Improve pairing workflow with QR code and URL display - Migrate hash cache from LocalStorage to IndexedDB for better scalability - Update signet-dashboard and mempool components **Root causes:** - N/A (feature implementations) **Correctifs:** - N/A (no bug fixes in this commit) **Evolutions:** - BIP39 mnemonic import: Support for 12/24 word English mnemonics with BIP32 derivation path m/44'/0'/0'/0/0 - Key protection: Password-based encryption of private keys at rest with unlock/lock functionality - Pairing workflow: QR code and URL display for device pairing, form-based word exchange between devices - IndexedDB migration: Hash cache moved from LocalStorage to IndexedDB to avoid size limitations - Global action bar: URL parameter support for navigation - Pairing connection: Enhanced pairing status management **Pages affectées:** - userwallet/src/utils/identity.ts - userwallet/src/utils/keyProtection.ts - userwallet/src/utils/sessionUnlockedKey.ts - userwallet/src/utils/indexedDbStorage.ts - userwallet/src/utils/cache.ts - userwallet/src/utils/pairing.ts - userwallet/src/components/UnlockScreen.tsx - userwallet/src/components/PairingDisplayScreen.tsx - userwallet/src/components/PairingSetupBlock.tsx - userwallet/src/components/GlobalActionBar.tsx - userwallet/src/components/HomeScreen.tsx - userwallet/src/components/ImportIdentityScreen.tsx - userwallet/src/components/DataExportImportScreen.tsx - userwallet/src/hooks/useIdentity.ts - userwallet/src/hooks/usePairingConnected.ts - userwallet/src/services/syncService.ts - userwallet/src/services/pairingConfirm.ts - userwallet/src/App.tsx - userwallet/package.json - userwallet/docs/specs.md - userwallet/docs/storage.md - userwallet/docs/synthese.md - signet-dashboard/public/*.html - signet-dashboard/public/app.js - signet-dashboard/public/styles.css - mempool (submodule updates) - hash_list.txt, hash_list_cache.txt, utxo_list.txt, utxo_list_cache.txt, fees_list.txt - features/*.md (documentation files)
190 lines
6.1 KiB
Markdown
190 lines
6.1 KiB
Markdown
# Stockage des données - UserWallet
|
||
|
||
**Author:** Équipe 4NK
|
||
**Date:** 2026-01-26
|
||
|
||
## Synthèse
|
||
|
||
- **Relais (api-relay)** : Stockage hybride (mémoire + `./data/messages.json`). Messages, `seenHashes`, signatures et clés persistés. Sauvegarde à l’arrêt (SIGINT/SIGTERM) et périodique (configurable via `SAVE_INTERVAL_SECONDS`).
|
||
- **Front (userwallet)** : LocalStorage (`userwallet_identity`, `userwallet_relays`, `userwallet_pairs`, `userwallet_hash_cache` ; legacy : `userwallet_keypair`, `userwallet_services`). IndexedDB : `hash_cache`, `userwallet_pairing_confirm`. Graphe contractuel en mémoire uniquement (`GraphResolver`).
|
||
|
||
## Stockage sur le relais (api-relay)
|
||
|
||
### Architecture
|
||
|
||
Le relais utilise un stockage **hybride** : mémoire + persistance sur disque.
|
||
|
||
### Structure de stockage
|
||
|
||
**En mémoire :**
|
||
- `messages: Map<string, StoredMessage>` - Messages chiffrés indexés par hash
|
||
- `signatures: Map<string, StoredSignature[]>` - Signatures indexées par hash de message
|
||
- `keys: Map<string, StoredKey[]>` - Clés de déchiffrement indexées par hash de message
|
||
- `seenHashes: Set<string>` - Hash vus pour déduplication
|
||
|
||
**Sur disque :**
|
||
- Fichier `{STORAGE_PATH}/messages.json` contenant :
|
||
- `messages`: Array de `[hash, StoredMessage]`
|
||
- `seenHashes`: Array de hash (string[])
|
||
- `signatures`: Array de `[hash, StoredSignature[]]`
|
||
- `keys`: Array de `[hash, StoredKey[]]`
|
||
|
||
### Persistance
|
||
|
||
- **Chargement** : Au démarrage, charge `messages.json` si présent (ENOENT = premier run, démarrage à vide).
|
||
- **Sauvegarde** : À l’arrêt (SIGINT/SIGTERM) et périodiquement si `SAVE_INTERVAL_SECONDS` > 0 (défaut 300 s).
|
||
|
||
### Format de données
|
||
|
||
**StoredMessage :**
|
||
```typescript
|
||
{
|
||
msg: {
|
||
hash: string,
|
||
message_chiffre: string,
|
||
datajson_public: {
|
||
services_uuid: string[],
|
||
types_uuid: string[],
|
||
timestamp?: number,
|
||
...
|
||
}
|
||
},
|
||
received_at: number,
|
||
relayed: boolean
|
||
}
|
||
```
|
||
|
||
**StoredSignature :**
|
||
```typescript
|
||
{
|
||
msg: {
|
||
signature: {
|
||
hash: string,
|
||
cle_publique: string,
|
||
signature: string,
|
||
nonce: string,
|
||
materiel?: object
|
||
},
|
||
hash_cible?: string
|
||
},
|
||
received_at: number,
|
||
relayed: boolean
|
||
}
|
||
```
|
||
|
||
**StoredKey :**
|
||
```typescript
|
||
{
|
||
msg: {
|
||
hash_message: string,
|
||
cle_de_chiffrement_message: {
|
||
algo: string,
|
||
params: object,
|
||
cle_chiffree?: string
|
||
},
|
||
df_ecdh_scannable: string
|
||
},
|
||
received_at: number,
|
||
relayed: boolean
|
||
}
|
||
```
|
||
|
||
### Configuration
|
||
|
||
- `STORAGE_PATH` : Chemin du répertoire de stockage (défaut: `./data`)
|
||
- `SAVE_INTERVAL_SECONDS` : Intervalle de sauvegarde périodique en secondes (défaut: 300). Mettre à 0 pour désactiver.
|
||
|
||
### Limitations actuelles
|
||
|
||
- Pas de base de données (SQLite/PostgreSQL recommandé en production)
|
||
- Pas de compression des données
|
||
|
||
## Stockage sur le front (userwallet)
|
||
|
||
### Architecture
|
||
|
||
Le front utilise **LocalStorage** du navigateur pour toutes les données locales.
|
||
|
||
### Structure de stockage
|
||
|
||
**Clés utilisées :**
|
||
|
||
1. **`userwallet_identity`** : Identité locale
|
||
```typescript
|
||
{
|
||
uuid: string,
|
||
privateKey: string,
|
||
publicKey: string,
|
||
name?: string,
|
||
t0_anniversaire: number,
|
||
version: string
|
||
}
|
||
```
|
||
|
||
2. **`userwallet_relays`** : Configuration des relais
|
||
```typescript
|
||
Array<{
|
||
endpoint: string,
|
||
priority: number,
|
||
enabled: boolean,
|
||
last_sync?: number
|
||
}>
|
||
```
|
||
|
||
3. **`userwallet_pairs`** : Configuration des pairs
|
||
```typescript
|
||
Array<{
|
||
uuid: string,
|
||
membres_parents_uuid: string[],
|
||
is_local: boolean,
|
||
can_sign: boolean,
|
||
publicKey?: string // clé publique identité de l'autre device (ECDH pairing)
|
||
}>
|
||
```
|
||
|
||
4. **`userwallet_hash_cache`** : Cache des hash vus (IndexedDB, store `kv`)
|
||
```typescript
|
||
string[] // Array de hash
|
||
```
|
||
|
||
5. **`userwallet_pairing_confirm`** : Confirmations de pairing (IndexedDB, store `kv`)
|
||
```typescript
|
||
Array<{ pairLocal: string, pairRemote: string, hash: string, version: number }>
|
||
```
|
||
|
||
6. **`userwallet_keypair`** : (Legacy) Paire de clés
|
||
7. **`userwallet_services`** : (Legacy) Services configurés
|
||
|
||
### Données en mémoire (non persistées)
|
||
|
||
- **Graphe contractuel** : Résolu dynamiquement depuis les messages synchronisés
|
||
- Services, Contrats, Champs, Actions, Membres, Pairs
|
||
- Stocké dans `GraphResolver.cache` (Map)
|
||
- Perdu au rechargement de page
|
||
|
||
### Limitations
|
||
|
||
- **Taille limitée** : LocalStorage a une limite (~5-10MB selon navigateur)
|
||
- **Pas de synchronisation** : Données locales uniquement
|
||
- **Sécurité** : Clés privées stockées en clair (à chiffrer avec mot de passe)
|
||
|
||
### Export / Import
|
||
|
||
- **Export** : Écran « Export / Import données » (`/data`). Télécharge un JSON (identité, relais, pairs, hash_cache, pairing_confirm, keypair, services). `hash_cache` et `pairing_confirm` lus depuis IndexedDB.
|
||
- **Import** : Même écran, bouton « Choisir un fichier ». Remplace les données locales (`hash_cache`, `pairing_confirm` écrits en IndexedDB si présents dans l’export) puis recharge la page.
|
||
|
||
### Protection par mot de passe
|
||
|
||
- **Activation** : Écran « Export / Import données » → section « Protection par mot de passe ». Mot de passe + confirmation (min. 8 caractères). Chiffrement AES-GCM (clé dérivée PBKDF2-HMAC-SHA256, 100k itérations).
|
||
- **Déverrouillage** : Si protection activée, écran « Déverrouiller » affiché tant que la session n’est pas ouverte. Mot de passe → clé en session, puis accès aux écrans normaux.
|
||
- **Verrouillage** : Bouton « Verrouiller » (accueil ou /data). Vide la session uniquement ; stockage chiffré inchangé.
|
||
- **Désactivation** : /data → « Désactiver la protection » (mot de passe actuel). Déchiffrement et réécriture de l’identité avec clé en clair.
|
||
|
||
### IndexedDB
|
||
|
||
- **Base** : `userwallet`, store `kv` (key/value).
|
||
- **Utilisation** : `hash_cache`, `userwallet_pairing_confirm` (confirmations de pairing). `utils/indexedDbStorage` expose `idbGet`, `idbSet`, `idbRemove`.
|
||
- **Migration** : Au premier `HashCache.init()`, si IndexedDB vide et `localStorage` contient `userwallet_hash_cache`, copie vers IndexedDB puis suppression du localStorage.
|
||
|
||
Pas de synchronisation cloud (hors scope, jamais).
|