feat(vault): vault storage (user-isolated, multi-account)

This commit is contained in:
Ada
2026-02-01 15:22:32 -08:00
parent 50e78c84c9
commit 3ffcc60ee8
3 changed files with 9 additions and 5 deletions

View File

@@ -67,9 +67,9 @@ export const API_ENDPOINTS = {
// ============================================================================= // =============================================================================
// Vault storage (user-isolated, multi-account) // Vault storage (user-isolated, multi-account)
// ============================================================================= // =============================================================================
// - AsyncStorage keys for vault state (S0 share, initialized flag). // - AsyncStorage keys for vault state (S0 share, initialized flag, mnemonic part backup).
// - User-scoped: each account has its own keys so vault state is isolated. // - User-scoped: each account has its own keys so vault/mnemonic state is isolated.
// - Store: use getVaultStorageKeys(userId) and write to INITIALIZED / SHARE_DEVICE. // - Store: use getVaultStorageKeys(userId) and write to INITIALIZED / SHARE_DEVICE / MNEMONIC_PART_LOCAL.
// - Clear: use same keys in multiRemove (e.g. MeScreen Reset Vault State). // - Clear: use same keys in multiRemove (e.g. MeScreen Reset Vault State).
// - Multi-account: same device, multiple users → each has independent vault (no cross-user leakage). // - Multi-account: same device, multiple users → each has independent vault (no cross-user leakage).
@@ -79,21 +79,24 @@ const VAULT_KEY_PREFIX = 'sentinel_vault';
export const VAULT_STORAGE_KEYS = { export const VAULT_STORAGE_KEYS = {
INITIALIZED: 'sentinel_vault_initialized', INITIALIZED: 'sentinel_vault_initialized',
SHARE_DEVICE: 'sentinel_vault_s0', SHARE_DEVICE: 'sentinel_vault_s0',
MNEMONIC_PART_LOCAL: 'sentinel_mnemonic_part_local',
} as const; } as const;
/** /**
* Returns vault storage keys for the given user (user isolation). * Returns vault storage keys for the given user (user isolation).
* - Use for: reading S0, writing S0 after mnemonic, clearing on Reset Vault State. * - Use for: reading/writing S0, mnemonic part backup, clearing on Reset Vault State.
* - userId null → guest namespace (_guest). userId set → per-user namespace (_u{userId}). * - userId null → guest namespace (_guest). userId set → per-user namespace (_u{userId}).
*/ */
export function getVaultStorageKeys(userId: number | string | null): { export function getVaultStorageKeys(userId: number | string | null): {
INITIALIZED: string; INITIALIZED: string;
SHARE_DEVICE: string; SHARE_DEVICE: string;
MNEMONIC_PART_LOCAL: string;
} { } {
const suffix = userId != null ? `_u${userId}` : '_guest'; const suffix = userId != null ? `_u${userId}` : '_guest';
return { return {
INITIALIZED: `${VAULT_KEY_PREFIX}_initialized${suffix}`, INITIALIZED: `${VAULT_KEY_PREFIX}_initialized${suffix}`,
SHARE_DEVICE: `${VAULT_KEY_PREFIX}_s0${suffix}`, SHARE_DEVICE: `${VAULT_KEY_PREFIX}_s0${suffix}`,
MNEMONIC_PART_LOCAL: `sentinel_mnemonic_part_local${suffix}`,
}; };
} }

View File

@@ -315,6 +315,7 @@ export default function MeScreen() {
await AsyncStorage.multiRemove([ await AsyncStorage.multiRemove([
vaultKeys.INITIALIZED, vaultKeys.INITIALIZED,
vaultKeys.SHARE_DEVICE, vaultKeys.SHARE_DEVICE,
vaultKeys.MNEMONIC_PART_LOCAL,
]); ]);
setResetVaultFeedback({ setResetVaultFeedback({
status: 'success', status: 'success',

View File

@@ -258,7 +258,7 @@ export default function VaultScreen() {
setProgressIndex(0); setProgressIndex(0);
progressAnim.setValue(0); progressAnim.setValue(0);
setTimeout(() => setShowMnemonic(true), 200); setTimeout(() => setShowMnemonic(true), 200);
AsyncStorage.setItem('sentinel_mnemonic_part_local', parts[0].join(' ')).catch(() => { AsyncStorage.setItem(vaultKeys.MNEMONIC_PART_LOCAL, parts[0].join(' ')).catch(() => {
// Best-effort local store; UI remains available // Best-effort local store; UI remains available
}); });
}; };