diff --git a/apps/mana/apps/web/src/lib/components/EncryptionIntroBanner.svelte b/apps/mana/apps/web/src/lib/components/EncryptionIntroBanner.svelte new file mode 100644 index 000000000..e40a7a99b --- /dev/null +++ b/apps/mana/apps/web/src/lib/components/EncryptionIntroBanner.svelte @@ -0,0 +1,215 @@ + + + +{#if visible} +
+{/if} + + diff --git a/apps/mana/apps/web/src/lib/data/crypto/index.ts b/apps/mana/apps/web/src/lib/data/crypto/index.ts index 65768cd78..eebf4466e 100644 --- a/apps/mana/apps/web/src/lib/data/crypto/index.ts +++ b/apps/mana/apps/web/src/lib/data/crypto/index.ts @@ -52,3 +52,5 @@ export { type VaultUnlockState, createVaultClient, } from './vault-client'; + +export { getVaultClient } from './vault-instance'; diff --git a/apps/mana/apps/web/src/lib/data/crypto/vault-instance.ts b/apps/mana/apps/web/src/lib/data/crypto/vault-instance.ts new file mode 100644 index 000000000..df8b4e5eb --- /dev/null +++ b/apps/mana/apps/web/src/lib/data/crypto/vault-instance.ts @@ -0,0 +1,39 @@ +/** + * Lazy-singleton wrapper around createVaultClient. + * + * Module-level vault clients are awkward to share because they need + * the auth store + the auth URL at construction time, neither of + * which are available at module-load (the auth store is initialised + * inside +layout.svelte's onMount). This wrapper builds the client + * the first time `getVaultClient()` is called and reuses it for all + * subsequent callers — root layout, settings page, future settings + * sub-pages, debug tools. + * + * The MemoryKeyProvider lives inside the vault client and is set + * via setKeyProvider during construction. Phase 3 already wired the + * record-helpers to read from getActiveKey(), so once any caller + * builds the singleton the rest of the data layer can encrypt and + * decrypt without knowing about the vault client at all. + */ + +import { createVaultClient, type VaultClient } from './vault-client'; +import { authStore } from '$lib/stores/auth.svelte'; +import { getManaAuthUrl } from '$lib/api/config'; + +let _instance: VaultClient | null = null; + +export function getVaultClient(): VaultClient { + if (!_instance) { + _instance = createVaultClient({ + authUrl: getManaAuthUrl(), + getToken: () => authStore.getAccessToken(), + }); + } + return _instance; +} + +/** Test-only reset hook so each integration test starts with a fresh + * client. Not exported from the crypto barrel — internal to this file. */ +export function _resetVaultInstanceForTesting(): void { + _instance = null; +} diff --git a/apps/mana/apps/web/src/routes/(app)/settings/security/+page.svelte b/apps/mana/apps/web/src/routes/(app)/settings/security/+page.svelte new file mode 100644 index 000000000..86513e729 --- /dev/null +++ b/apps/mana/apps/web/src/routes/(app)/settings/security/+page.svelte @@ -0,0 +1,397 @@ + + + ++ Verschlüsselung deiner Inhalte auf diesem Gerät. Sensitive Felder werden mit AES-GCM-256 + verschlüsselt, bevor sie in die lokale Datenbank geschrieben werden. +
++ Dein persönlicher Schlüssel ist auf diesem Gerät geladen. {totalEncryptedFields} + Felder über {encryptedTables.length} Tabellen werden verschlüsselt gespeichert. +
+ {:else if vaultState.status === 'locked'} ++ Dein Schlüssel ist nicht geladen. Verschlüsselte Inhalte können nicht gelesen werden, bis du + dich erneut anmeldest oder den Schlüssel manuell lädst. +
+ + {:else} ++ Es gab ein Problem beim Laden deines Verschlüsselungsschlüssels. Bitte melde dich neu an + oder prüfe deine Internetverbindung. +
+ + {/if} ++ Welche Spalten in welchen Tabellen verschlüsselt am Gerät liegen. Strukturelle Metadaten (IDs, + Zeitstempel, Status-Flags) bleiben absichtlich im Klartext, damit Indizes, Sortierungen und + Sync weiter funktionieren. +
++ Vorsicht: Beim Rotieren wird ein neuer Schlüssel generiert. Daten, die mit + dem alten Schlüssel verschlüsselt wurden, sind danach nicht mehr lesbar — es sei denn, sie + wurden vorher entschlüsselt und mit dem neuen Schlüssel neu geschrieben. Mana führt diesen + Re-Encrypt-Schritt aktuell nicht automatisch durch. +
++ Wann verwenden? Wenn du den Verdacht hast, dass dein Gerät kompromittiert wurde, oder als + regelmäßige Sicherheitspraxis nach einem Login von einem unbekannten Ort. +
+ {#if !confirmRotate} + + {:else} ++ Mana speichert deinen Schlüssel verschlüsselt auf dem Server (mit einer separaten + Schlüssel-Verschlüsselungs-Schlüssel-Strategie), damit du dich von neuen Geräten anmelden + kannst. Das bedeutet: +
+