diff --git a/web/crypto/aes-gcm-crypto-utils.js b/web/crypto/aes-gcm-crypto-utils.js
--- a/web/crypto/aes-gcm-crypto-utils.js
+++ b/web/crypto/aes-gcm-crypto-utils.js
@@ -1,5 +1,9 @@
 // @flow
 
+import t, { type TInterface, type TUnion } from 'tcomb';
+
+import { tShape } from 'lib/utils/validation-utils.js';
+
 const ENCRYPTION_ALGORITHM = 'AES-GCM';
 const ENCRYPTION_KEY_USAGES: $ReadOnlyArray<CryptoKey$Usages> = [
   'encrypt',
@@ -11,6 +15,45 @@
   +ciphertext: Uint8Array,
 };
 
+export const encryptedAESDataValidator: TInterface<EncryptedData> =
+  tShape<EncryptedData>({
+    iv: t.irreducible('Uint8Array', x => x instanceof Uint8Array),
+    ciphertext: t.irreducible('Uint8Array', x => x instanceof Uint8Array),
+  });
+
+export const cryptoKeyValidator: TInterface<CryptoKey> = tShape<CryptoKey>({
+  algorithm: t.Object,
+  extractable: t.Boolean,
+  type: t.String,
+  usages: t.list(t.String),
+});
+
+export const subtleCrypto$JsonWebKeyValidator: TInterface<SubtleCrypto$JsonWebKey> =
+  tShape({
+    alg: t.maybe(t.String),
+    crv: t.maybe(t.String),
+    d: t.maybe(t.String),
+    dp: t.maybe(t.String),
+    dq: t.maybe(t.String),
+    e: t.maybe(t.String),
+    ext: t.maybe(t.Boolean),
+    k: t.maybe(t.String),
+    key_ops: t.maybe(t.list(t.String)),
+    kty: t.maybe(t.String),
+    n: t.maybe(t.String),
+    oth: t.maybe(t.list(t.Object)),
+    p: t.maybe(t.String),
+    q: t.maybe(t.String),
+    qi: t.maybe(t.String),
+    use: t.maybe(t.String),
+    x: t.maybe(t.String),
+    y: t.maybe(t.String),
+  });
+
+export const extendedCryptoKeyValidator: TUnion<
+  CryptoKey | SubtleCrypto$JsonWebKey,
+> = t.union([cryptoKeyValidator, subtleCrypto$JsonWebKeyValidator]);
+
 function generateCryptoKey({
   extractable,
 }: {
@@ -26,7 +69,7 @@
   );
 }
 
-function generateIV(): BufferSource {
+function generateIV(): Uint8Array {
   return crypto.getRandomValues(new Uint8Array(12));
 }
 
diff --git a/web/push-notif/notif-crypto-utils.js b/web/push-notif/notif-crypto-utils.js
--- a/web/push-notif/notif-crypto-utils.js
+++ b/web/push-notif/notif-crypto-utils.js
@@ -4,10 +4,12 @@
 import type { EncryptResult } from '@commapp/olm';
 import invariant from 'invariant';
 import localforage from 'localforage';
+import uuid from 'uuid';
 
 import {
   olmEncryptedMessageTypes,
   type NotificationsOlmDataType,
+  type PickledOLMAccount,
 } from 'lib/types/crypto-types.js';
 import type {
   PlainTextWebNotification,
@@ -15,12 +17,18 @@
 } from 'lib/types/notif-types.js';
 import { getCookieIDFromCookie } from 'lib/utils/cookie-utils.js';
 import { getMessageForException } from 'lib/utils/errors.js';
+import { promiseAll } from 'lib/utils/promises.js';
+import { assertWithValidator } from 'lib/utils/validation-utils.js';
 
 import {
   type EncryptedData,
   decryptData,
   encryptData,
   importJWKKey,
+  exportKeyToJWK,
+  generateCryptoKey,
+  encryptedAESDataValidator,
+  extendedCryptoKeyValidator,
 } from '../crypto/aes-gcm-crypto-utils.js';
 import { initOlm } from '../olm/olm-utils.js';
 import {
@@ -40,6 +48,13 @@
   +staffCanSee: boolean,
 };
 
+export type NotificationAccountWithPicklingKey = {
+  +notificationAccount: olm.Account,
+  +picklingKey: string,
+  +synchronizationValue: ?string,
+  +accountEncryptionKey?: CryptoKey,
+};
+
 type DecryptionResult<T> = {
   +newPendingSessionUpdate: string,
   +newUpdateCreationTimestamp: number,
@@ -52,6 +67,7 @@
 const INDEXED_DB_KEYSERVER_PREFIX = 'keyserver';
 const INDEXED_DB_KEY_SEPARATOR = ':';
 const INDEXED_DB_DEVICE_PREFIX = 'device';
+const INDEXED_DB_NOTIFS_SYNC_KEY = 'notifsSyncKey';
 
 // This constant is only used to migrate the existing notifications
 // session with production keyserver to new IndexedDB key format. This
@@ -63,6 +79,146 @@
   '256';
 
 const INDEXED_DB_UNREAD_COUNT_SUFFIX = 'unreadCount';
+const INDEXED_DB_NOTIFS_ACCOUNT_KEY = 'notificationAccount';
+const INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL =
+  'notificationAccountEncryptionKey';
+
+async function deserializeEncryptedData<T>(
+  encryptedData: EncryptedData,
+  encryptionKey: CryptoKey,
+): Promise<T> {
+  const serializedData = await decryptData(encryptedData, encryptionKey);
+  const data: T = JSON.parse(new TextDecoder().decode(serializedData));
+  return data;
+}
+
+async function serializeUnencryptedData<T>(
+  data: T,
+  encryptionKey: CryptoKey,
+): Promise<EncryptedData> {
+  const dataAsString = JSON.stringify(data);
+  invariant(
+    dataAsString,
+    'Attempt to serialize null or undefined is forbidden',
+  );
+  return await encryptData(
+    new TextEncoder().encode(dataAsString),
+    encryptionKey,
+  );
+}
+
+async function validateCryptoKey(
+  cryptoKey: CryptoKey | SubtleCrypto$JsonWebKey,
+): Promise<CryptoKey> {
+  if (!isDesktopSafari) {
+    return ((cryptoKey: any): CryptoKey);
+  }
+  return await importJWKKey(((cryptoKey: any): SubtleCrypto$JsonWebKey));
+}
+
+async function getCryptoKeyPersistentForm(
+  cryptoKey: CryptoKey,
+): Promise<CryptoKey | SubtleCrypto$JsonWebKey> {
+  if (!isDesktopSafari) {
+    return cryptoKey;
+  }
+
+  // Safari doesn't support structured clone algorithm in service
+  // worker context so we have to store CryptoKey as JSON
+  return await exportKeyToJWK(cryptoKey);
+}
+
+async function persistNotifsAccountWithOlmData(input: {
+  +olmDataKey?: string,
+  +olmEncryptionKeyDBLabel?: string,
+  +olmData?: ?NotificationsOlmDataType,
+  +encryptionKey?: ?CryptoKey,
+  +accountEncryptionKey?: ?CryptoKey,
+  +accountWithPicklingKey?: PickledOLMAccount,
+  +synchronizationValue: ?string,
+  +forceWrite: boolean,
+}): Promise<void> {
+  const {
+    olmData,
+    olmDataKey,
+    accountEncryptionKey,
+    accountWithPicklingKey,
+    encryptionKey,
+    synchronizationValue,
+    olmEncryptionKeyDBLabel,
+    forceWrite,
+  } = input;
+
+  const shouldPersistOlmData =
+    olmDataKey && olmData && (encryptionKey || olmEncryptionKeyDBLabel);
+  const shouldPersistAccount = !!accountWithPicklingKey;
+
+  if (!shouldPersistOlmData && !shouldPersistAccount) {
+    return;
+  }
+
+  const serializationPromises: {
+    [string]: Promise<EncryptedData | CryptoKey | SubtleCrypto$JsonWebKey>,
+  } = {};
+
+  if (olmDataKey && olmData && encryptionKey) {
+    serializationPromises[olmDataKey] =
+      serializeUnencryptedData<NotificationsOlmDataType>(
+        olmData,
+        encryptionKey,
+      );
+  } else if (olmData && olmDataKey && olmEncryptionKeyDBLabel) {
+    const newEncryptionKey = await generateCryptoKey({
+      extractable: isDesktopSafari,
+    });
+
+    serializationPromises[olmDataKey] =
+      serializeUnencryptedData<NotificationsOlmDataType>(
+        olmData,
+        newEncryptionKey,
+      );
+
+    serializationPromises[olmEncryptionKeyDBLabel] =
+      getCryptoKeyPersistentForm(newEncryptionKey);
+  }
+
+  if (accountWithPicklingKey && accountEncryptionKey) {
+    serializationPromises[INDEXED_DB_NOTIFS_ACCOUNT_KEY] =
+      serializeUnencryptedData<PickledOLMAccount>(
+        accountWithPicklingKey,
+        accountEncryptionKey,
+      );
+  } else if (accountWithPicklingKey) {
+    const newEncryptionKey = await generateCryptoKey({
+      extractable: isDesktopSafari,
+    });
+
+    serializationPromises[INDEXED_DB_NOTIFS_ACCOUNT_KEY] =
+      serializeUnencryptedData<PickledOLMAccount>(
+        accountWithPicklingKey,
+        newEncryptionKey,
+      );
+
+    serializationPromises[INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL] =
+      getCryptoKeyPersistentForm(newEncryptionKey);
+  }
+
+  const setMultipleItemsInput = await promiseAll(serializationPromises);
+  const newSynchronizationValue = uuid.v4();
+
+  try {
+    await localforage.setMultipleItems(
+      setMultipleItemsInput,
+      INDEXED_DB_NOTIFS_SYNC_KEY,
+      synchronizationValue,
+      newSynchronizationValue,
+      forceWrite,
+    );
+  } catch (e) {
+    // likely shared worker persisted its own data
+    console.log(e);
+  }
+}
 
 async function decryptWebNotification(
   encryptedNotification: EncryptedWebNotification,
@@ -319,13 +475,21 @@
   const olmEncryptionKeyDBLabel =
     getOlmEncryptionKeyDBLabelForDeviceID(deviceID);
 
-  let encryptedOlmData, encryptionKey;
+  let encryptedOlmData, encryptionKey, synchronizationValue;
   try {
-    [encryptedOlmData, encryptionKey] = await Promise.all([
-      localforage.getItem<EncryptedData>(olmDataKey),
-      retrieveEncryptionKey(olmEncryptionKeyDBLabel),
-      initOlm(),
-    ]);
+    const {
+      values: {
+        [olmDataKey]: fetchedEncryptedOlmData,
+        [olmEncryptionKeyDBLabel]: fetchedEncryptionKey,
+      },
+      synchronizationValue: fetchedSynchronizationValue,
+    } = await localforage.getMultipleItems<{
+      +[string]: ?EncryptedData | ?CryptoKey | ?SubtleCrypto$JsonWebKey,
+    }>([olmDataKey, olmEncryptionKeyDBLabel], INDEXED_DB_NOTIFS_SYNC_KEY);
+
+    encryptedOlmData = fetchedEncryptedOlmData;
+    encryptionKey = fetchedEncryptionKey;
+    synchronizationValue = fetchedSynchronizationValue;
   } catch (e) {
     throw new Error(
       `Failed to fetch olm session from IndexedDB for device: ${deviceID}. Details: ${
@@ -338,13 +502,22 @@
     throw new Error(`Session with device: ${deviceID} not initialized.`);
   }
 
+  const validatedEncryptedOlmData = assertWithValidator(
+    encryptedOlmData,
+    encryptedAESDataValidator,
+  );
+  const validatedEncryptionKey = await validateCryptoKey(
+    assertWithValidator(encryptionKey, extendedCryptoKeyValidator),
+  );
+
   let encryptedNotification;
   try {
     encryptedNotification = await encryptNotificationWithOlmSession(
       payload,
-      encryptedOlmData,
+      validatedEncryptedOlmData,
       olmDataKey,
-      encryptionKey,
+      validatedEncryptionKey,
+      synchronizationValue,
     );
   } catch (e) {
     throw new Error(
@@ -361,6 +534,7 @@
   encryptedOlmData: EncryptedData,
   olmDataKey: string,
   encryptionKey: CryptoKey,
+  synchronizationValue: ?string,
 ): Promise<EncryptResult> {
   const serializedOlmData = await decryptData(encryptedOlmData, encryptionKey);
   const {
@@ -388,10 +562,70 @@
     encryptionKey,
   );
 
-  await localforage.setItem(olmDataKey, updatedEncryptedSession);
+  const newSynchronizationValue = uuid.v4();
+  await localforage.setMultipleItems(
+    { [olmDataKey]: updatedEncryptedSession },
+    INDEXED_DB_NOTIFS_SYNC_KEY,
+    synchronizationValue,
+    newSynchronizationValue,
+    // This method (encryptNotification) is expected to be called
+    // exclusively from the shared worker which must always win race
+    // condition against push notifications service-worker.
+    true,
+  );
+
   return encryptedNotification;
 }
 
+// notifications account manipulation
+
+async function getNotifsCryptoAccount(): Promise<NotificationAccountWithPicklingKey> {
+  const {
+    values: {
+      [INDEXED_DB_NOTIFS_ACCOUNT_KEY]: encryptedNotifsAccount,
+      [INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL]:
+        notifsAccountEncryptionKey,
+    },
+    synchronizationValue,
+  } = await localforage.getMultipleItems<{
+    +notificationAccount: ?EncryptedData,
+    +notificationAccountEncryptionKey: ?CryptoKey | ?SubtleCrypto$JsonWebKey,
+  }>(
+    [
+      INDEXED_DB_NOTIFS_ACCOUNT_KEY,
+      INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL,
+    ],
+    INDEXED_DB_NOTIFS_SYNC_KEY,
+  );
+
+  if (!encryptedNotifsAccount || !notifsAccountEncryptionKey) {
+    throw new Error(
+      'Attempt to retrieve notifs olm account but account not created.',
+    );
+  }
+
+  const validatedNotifsAccountEncryptionKey = await validateCryptoKey(
+    notifsAccountEncryptionKey,
+  );
+
+  const pickledOLMAccount = await deserializeEncryptedData<PickledOLMAccount>(
+    encryptedNotifsAccount,
+    validatedNotifsAccountEncryptionKey,
+  );
+
+  const { pickledAccount, picklingKey } = pickledOLMAccount;
+
+  const notificationAccount = new olm.Account();
+  notificationAccount.unpickle(picklingKey, pickledAccount);
+
+  return {
+    notificationAccount,
+    picklingKey,
+    synchronizationValue,
+    accountEncryptionKey: validatedNotifsAccountEncryptionKey,
+  };
+}
+
 async function retrieveEncryptionKey(
   encryptionKeyDBLabel: string,
 ): Promise<?CryptoKey> {
@@ -408,6 +642,22 @@
   return await importJWKKey(persistedCryptoKey);
 }
 
+async function persistEncryptionKey(
+  encryptionKeyDBLabel: string,
+  encryptionKey: CryptoKey,
+): Promise<void> {
+  let cryptoKeyPersistentForm;
+  if (isDesktopSafari) {
+    // Safari doesn't support structured clone algorithm in service
+    // worker context so we have to store CryptoKey as JSON
+    cryptoKeyPersistentForm = await exportKeyToJWK(encryptionKey);
+  } else {
+    cryptoKeyPersistentForm = encryptionKey;
+  }
+
+  await localforage.setItem(encryptionKeyDBLabel, cryptoKeyPersistentForm);
+}
+
 async function getNotifsOlmSessionDBKeys(keyserverID?: string): Promise<{
   +olmDataKey: string,
   +encryptionKeyDBKey: string,
@@ -643,4 +893,8 @@
   migrateLegacyOlmNotificationsSessions,
   updateNotifsUnreadCountStorage,
   queryNotifsUnreadCountStorage,
+  getNotifsCryptoAccount,
+  persistEncryptionKey,
+  retrieveEncryptionKey,
+  persistNotifsAccountWithOlmData,
 };
diff --git a/web/shared-worker/worker/worker-crypto.js b/web/shared-worker/worker/worker-crypto.js
--- a/web/shared-worker/worker/worker-crypto.js
+++ b/web/shared-worker/worker/worker-crypto.js
@@ -44,17 +44,15 @@
   getSQLiteQueryExecutor,
   getPlatformDetails,
 } from './worker-database.js';
-import {
-  encryptData,
-  exportKeyToJWK,
-  generateCryptoKey,
-} from '../../crypto/aes-gcm-crypto-utils.js';
 import {
   getOlmDataKeyForCookie,
   getOlmEncryptionKeyDBLabelForCookie,
   getOlmDataKeyForDeviceID,
   getOlmEncryptionKeyDBLabelForDeviceID,
   encryptNotification,
+  type NotificationAccountWithPicklingKey,
+  getNotifsCryptoAccount,
+  persistNotifsAccountWithOlmData,
 } from '../../push-notif/notif-crypto-utils.js';
 import {
   type WorkerRequestMessage,
@@ -64,7 +62,6 @@
   type LegacyCryptoStore,
 } from '../../types/worker-types.js';
 import type { OlmPersistSession } from '../types/sqlite-query-executor.js';
-import { isDesktopSafari } from '../utils/db-utils.js';
 
 type OlmSession = { +session: olm.Session, +version: number };
 type OlmSessions = {
@@ -75,8 +72,6 @@
   +contentAccountPickleKey: string,
   +contentAccount: olm.Account,
   +contentSessions: OlmSessions,
-  +notificationAccountPickleKey: string,
-  +notificationAccount: olm.Account,
 };
 
 let cryptoStore: ?WorkerCryptoStore = null;
@@ -86,7 +81,10 @@
   cryptoStore = null;
 }
 
-function persistCryptoStore(withoutTransaction: boolean = false) {
+async function persistCryptoStore(
+  notifsCryptoAccount?: NotificationAccountWithPicklingKey,
+  withoutTransaction: boolean = false,
+) {
   const sqliteQueryExecutor = getSQLiteQueryExecutor();
   const dbModule = getDBModule();
   if (!sqliteQueryExecutor || !dbModule) {
@@ -98,13 +96,8 @@
     throw new Error("Couldn't persist crypto store because it doesn't exist");
   }
 
-  const {
-    contentAccountPickleKey,
-    contentAccount,
-    contentSessions,
-    notificationAccountPickleKey,
-    notificationAccount,
-  } = cryptoStore;
+  const { contentAccountPickleKey, contentAccount, contentSessions } =
+    cryptoStore;
 
   const pickledContentAccount: PickledOLMAccount = {
     picklingKey: contentAccountPickleKey,
@@ -119,11 +112,6 @@
     version: sessionData.version,
   }));
 
-  const pickledNotificationAccount: PickledOLMAccount = {
-    picklingKey: notificationAccountPickleKey,
-    pickledAccount: notificationAccount.pickle(notificationAccountPickleKey),
-  };
-
   try {
     if (!withoutTransaction) {
       sqliteQueryExecutor.beginTransaction();
@@ -135,10 +123,27 @@
     for (const pickledSession of pickledContentSessions) {
       sqliteQueryExecutor.storeOlmPersistSession(pickledSession);
     }
-    sqliteQueryExecutor.storeOlmPersistAccount(
-      sqliteQueryExecutor.getNotifsAccountID(),
-      JSON.stringify(pickledNotificationAccount),
-    );
+    if (notifsCryptoAccount) {
+      const {
+        notificationAccount,
+        picklingKey,
+        synchronizationValue,
+        accountEncryptionKey,
+      } = notifsCryptoAccount;
+
+      const pickledAccount = notificationAccount.pickle(picklingKey);
+      const accountWithPicklingKey: PickledOLMAccount = {
+        pickledAccount,
+        picklingKey,
+      };
+
+      await persistNotifsAccountWithOlmData({
+        accountEncryptionKey,
+        accountWithPicklingKey,
+        synchronizationValue,
+        forceWrite: true,
+      });
+    }
     if (!withoutTransaction) {
       sqliteQueryExecutor.commitTransaction();
     }
@@ -160,10 +165,13 @@
     throw new Error('Crypto account not initialized');
   }
 
-  const { notificationAccountPickleKey, notificationAccount } = cryptoStore;
-  const encryptionKey = await generateCryptoKey({
-    extractable: isDesktopSafari,
-  });
+  const notificationAccountWithPicklingKey = await getNotifsCryptoAccount();
+  const {
+    notificationAccount,
+    picklingKey,
+    synchronizationValue,
+    accountEncryptionKey,
+  } = notificationAccountWithPicklingKey;
 
   const notificationsPrekey = notificationsInitializationInfo.prekey;
   const session = new olm.Session();
@@ -189,46 +197,40 @@
     JSON.stringify(initialEncryptedMessageContent),
   );
 
-  const mainSession = session.pickle(notificationAccountPickleKey);
+  const mainSession = session.pickle(
+    notificationAccountWithPicklingKey.picklingKey,
+  );
   const notificationsOlmData: NotificationsOlmDataType = {
     mainSession,
     pendingSessionUpdate: mainSession,
     updateCreationTimestamp: Date.now(),
-    picklingKey: notificationAccountPickleKey,
+    picklingKey,
   };
-  const encryptedOlmData = await encryptData(
-    new TextEncoder().encode(JSON.stringify(notificationsOlmData)),
-    encryptionKey,
-  );
-
-  const persistEncryptionKeyPromise = (async () => {
-    let cryptoKeyPersistentForm;
-    if (isDesktopSafari) {
-      // Safari doesn't support structured clone algorithm in service
-      // worker context so we have to store CryptoKey as JSON
-      cryptoKeyPersistentForm = await exportKeyToJWK(encryptionKey);
-    } else {
-      cryptoKeyPersistentForm = encryptionKey;
-    }
 
-    await localforage.setItem(
-      dataEncryptionKeyDBLabel,
-      cryptoKeyPersistentForm,
-    );
-  })();
+  const pickledAccount = notificationAccount.pickle(picklingKey);
+  const accountWithPicklingKey: PickledOLMAccount = {
+    pickledAccount,
+    picklingKey,
+  };
 
-  await Promise.all([
-    localforage.setItem(dataPersistenceKey, encryptedOlmData),
-    persistEncryptionKeyPromise,
-  ]);
+  await persistNotifsAccountWithOlmData({
+    accountEncryptionKey,
+    accountWithPicklingKey,
+    olmDataKey: dataPersistenceKey,
+    olmData: notificationsOlmData,
+    olmEncryptionKeyDBLabel: dataEncryptionKeyDBLabel,
+    synchronizationValue,
+    forceWrite: true,
+  });
 
   return { message, messageType };
 }
 
-function getOrCreateOlmAccount(accountIDInDB: number): {
+async function getOrCreateOlmAccount(accountIDInDB: number): Promise<{
   +picklingKey: string,
   +account: olm.Account,
-} {
+  +synchronizationValue?: ?string,
+}> {
   const sqliteQueryExecutor = getSQLiteQueryExecutor();
   const dbModule = getDBModule();
   if (!sqliteQueryExecutor || !dbModule) {
@@ -246,6 +248,31 @@
     throw new Error(getProcessingStoreOpsExceptionMessage(err, dbModule));
   }
 
+  const maybeNotifsCryptoAccount: ?NotificationAccountWithPicklingKey =
+    await (async () => {
+      if (accountIDInDB !== sqliteQueryExecutor.getNotifsAccountID()) {
+        return undefined;
+      }
+      try {
+        return await getNotifsCryptoAccount();
+      } catch (e) {
+        return undefined;
+      }
+    })();
+
+  if (maybeNotifsCryptoAccount) {
+    const {
+      notificationAccount,
+      picklingKey: notificationAccountPicklingKey,
+      synchronizationValue,
+    } = maybeNotifsCryptoAccount;
+    return {
+      account: notificationAccount,
+      picklingKey: notificationAccountPicklingKey,
+      synchronizationValue,
+    };
+  }
+
   if (accountDBString.isNull) {
     picklingKey = uuid.v4();
     account.create();
@@ -255,6 +282,10 @@
     account.unpickle(picklingKey, dbAccount.pickledAccount);
   }
 
+  if (accountIDInDB === sqliteQueryExecutor.getNotifsAccountID()) {
+    return { picklingKey, account, synchronizationValue: uuid.v4() };
+  }
+
   return { picklingKey, account };
 }
 
@@ -316,13 +347,15 @@
         initialCryptoStore.primaryAccount,
       ),
       contentSessions: {},
-      notificationAccountPickleKey:
-        initialCryptoStore.notificationAccount.picklingKey,
+    };
+    const notifsCryptoAccount = {
+      picklingKey: initialCryptoStore.notificationAccount.picklingKey,
       notificationAccount: unpickleInitialCryptoStoreAccount(
         initialCryptoStore.notificationAccount,
       ),
+      synchronizationValue: uuid.v4(),
     };
-    persistCryptoStore();
+    await persistCryptoStore(notifsCryptoAccount);
     return;
   }
 
@@ -352,12 +385,13 @@
   return undefined;
 }
 
-function getSignedIdentityKeysBlob(): SignedIdentityKeysBlob {
+async function getSignedIdentityKeysBlob(): Promise<SignedIdentityKeysBlob> {
   if (!cryptoStore) {
     throw new Error('Crypto account not initialized');
   }
 
-  const { contentAccount, notificationAccount } = cryptoStore;
+  const { contentAccount } = cryptoStore;
+  const { notificationAccount } = await getNotifsCryptoAccount();
 
   const identityKeysBlob: IdentityKeysBlob = {
     notificationIdentityPublicKeys: JSON.parse(
@@ -375,22 +409,25 @@
   return signedIdentityKeysBlob;
 }
 
-function getNewDeviceKeyUpload(): IdentityNewDeviceKeyUpload {
+async function getNewDeviceKeyUpload(): Promise<IdentityNewDeviceKeyUpload> {
   if (!cryptoStore) {
     throw new Error('Crypto account not initialized');
   }
-  const { contentAccount, notificationAccount } = cryptoStore;
-
-  const signedIdentityKeysBlob = getSignedIdentityKeysBlob();
+  const { contentAccount } = cryptoStore;
+  const [notifsCryptoAccount, signedIdentityKeysBlob] = await Promise.all([
+    getNotifsCryptoAccount(),
+    getSignedIdentityKeysBlob(),
+  ]);
 
   const primaryAccountKeysSet = retrieveAccountKeysSet(contentAccount);
-  const notificationAccountKeysSet =
-    retrieveAccountKeysSet(notificationAccount);
+  const notificationAccountKeysSet = retrieveAccountKeysSet(
+    notifsCryptoAccount.notificationAccount,
+  );
 
   contentAccount.mark_keys_as_published();
-  notificationAccount.mark_keys_as_published();
+  notifsCryptoAccount.notificationAccount.mark_keys_as_published();
 
-  persistCryptoStore();
+  await persistCryptoStore(notifsCryptoAccount);
 
   return {
     keyPayload: signedIdentityKeysBlob.payload,
@@ -404,20 +441,22 @@
   };
 }
 
-function getExistingDeviceKeyUpload(): IdentityExistingDeviceKeyUpload {
+async function getExistingDeviceKeyUpload(): Promise<IdentityExistingDeviceKeyUpload> {
   if (!cryptoStore) {
     throw new Error('Crypto account not initialized');
   }
-  const { contentAccount, notificationAccount } = cryptoStore;
-
-  const signedIdentityKeysBlob = getSignedIdentityKeysBlob();
+  const { contentAccount } = cryptoStore;
+  const [notifsCryptoAccount, signedIdentityKeysBlob] = await Promise.all([
+    getNotifsCryptoAccount(),
+    getSignedIdentityKeysBlob(),
+  ]);
 
   const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } =
     retrieveIdentityKeysAndPrekeys(contentAccount);
   const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } =
-    retrieveIdentityKeysAndPrekeys(notificationAccount);
+    retrieveIdentityKeysAndPrekeys(notifsCryptoAccount.notificationAccount);
 
-  persistCryptoStore();
+  await persistCryptoStore(notifsCryptoAccount);
 
   return {
     keyPayload: signedIdentityKeysBlob.payload,
@@ -470,30 +509,36 @@
       throw new Error('Database not initialized');
     }
 
-    const contentAccountResult = getOrCreateOlmAccount(
-      sqliteQueryExecutor.getContentAccountID(),
-    );
-    const notificationAccountResult = getOrCreateOlmAccount(
-      sqliteQueryExecutor.getNotifsAccountID(),
+    const [contentAccountResult, notificationAccountResult] = await Promise.all(
+      [
+        getOrCreateOlmAccount(sqliteQueryExecutor.getContentAccountID()),
+        getOrCreateOlmAccount(sqliteQueryExecutor.getNotifsAccountID()),
+      ],
     );
+
     const contentSessions = getOlmSessions(contentAccountResult.picklingKey);
 
     cryptoStore = {
       contentAccountPickleKey: contentAccountResult.picklingKey,
       contentAccount: contentAccountResult.account,
       contentSessions,
-      notificationAccountPickleKey: notificationAccountResult.picklingKey,
+    };
+    const notifsCryptoAccount = {
+      picklingKey: notificationAccountResult.picklingKey,
       notificationAccount: notificationAccountResult.account,
+      synchronizationValue: notificationAccountResult.synchronizationValue,
     };
 
-    persistCryptoStore();
+    await persistCryptoStore(notifsCryptoAccount);
   },
   async getUserPublicKey(): Promise<ClientPublicKeys> {
     if (!cryptoStore) {
       throw new Error('Crypto account not initialized');
     }
-    const { contentAccount, notificationAccount } = cryptoStore;
-    const { payload, signature } = getSignedIdentityKeysBlob();
+    const { contentAccount } = cryptoStore;
+    const [{ notificationAccount }, { payload, signature }] = await Promise.all(
+      [getNotifsCryptoAccount(), getSignedIdentityKeysBlob()],
+    );
 
     return {
       primaryIdentityPublicKeys: JSON.parse(contentAccount.identity_keys()),
@@ -514,7 +559,7 @@
     }
     const encryptedContent = olmSession.session.encrypt(content);
 
-    persistCryptoStore();
+    await persistCryptoStore();
 
     return {
       message: encryptedContent.body,
@@ -556,7 +601,7 @@
         deviceID,
         JSON.stringify(result),
       );
-      persistCryptoStore(true);
+      await persistCryptoStore(undefined, true);
       sqliteQueryExecutor.commitTransaction();
     } catch (e) {
       sqliteQueryExecutor.rollbackTransaction();
@@ -593,7 +638,7 @@
       encryptedData.message,
     );
 
-    persistCryptoStore();
+    await persistCryptoStore();
 
     return result;
   },
@@ -634,7 +679,7 @@
     sqliteQueryExecutor.beginTransaction();
     try {
       sqliteQueryExecutor.addInboundP2PMessage(receivedMessage);
-      persistCryptoStore(true);
+      await persistCryptoStore(undefined, true);
       sqliteQueryExecutor.commitTransaction();
     } catch (e) {
       sqliteQueryExecutor.rollbackTransaction();
@@ -680,7 +725,7 @@
       session,
       version: sessionVersion,
     };
-    persistCryptoStore();
+    await persistCryptoStore();
 
     return initialEncryptedMessage;
   },
@@ -722,7 +767,7 @@
       session,
       version: newSessionVersion,
     };
-    persistCryptoStore();
+    await persistCryptoStore();
 
     const encryptedData: EncryptedData = {
       message: initialEncryptedData.body,
@@ -844,7 +889,8 @@
     if (!cryptoStore) {
       throw new Error('Crypto account not initialized');
     }
-    const { contentAccount, notificationAccount } = cryptoStore;
+    const { contentAccount } = cryptoStore;
+    const notifsCryptoAccount = await getNotifsCryptoAccount();
 
     const contentOneTimeKeys = getAccountOneTimeKeys(
       contentAccount,
@@ -853,12 +899,12 @@
     contentAccount.mark_keys_as_published();
 
     const notificationsOneTimeKeys = getAccountOneTimeKeys(
-      notificationAccount,
+      notifsCryptoAccount.notificationAccount,
       numberOfKeys,
     );
-    notificationAccount.mark_keys_as_published();
+    notifsCryptoAccount.notificationAccount.mark_keys_as_published();
 
-    persistCryptoStore();
+    await persistCryptoStore(notifsCryptoAccount);
 
     return { contentOneTimeKeys, notificationsOneTimeKeys };
   },
@@ -876,26 +922,27 @@
     if (!cryptoStore) {
       throw new Error('Crypto account not initialized');
     }
-    const { contentAccount, notificationAccount } = cryptoStore;
+    const { contentAccount } = cryptoStore;
+    const notifsCryptoAccount = await getNotifsCryptoAccount();
 
     // Content and notification accounts' keys are always rotated at the same
     // time so we only need to check one of them.
     if (shouldRotatePrekey(contentAccount)) {
       contentAccount.generate_prekey();
-      notificationAccount.generate_prekey();
+      notifsCryptoAccount.notificationAccount.generate_prekey();
     }
     if (shouldForgetPrekey(contentAccount)) {
       contentAccount.forget_old_prekey();
-      notificationAccount.forget_old_prekey();
+      notifsCryptoAccount.notificationAccount.forget_old_prekey();
     }
-    persistCryptoStore();
+    await persistCryptoStore(notifsCryptoAccount);
 
     if (!contentAccount.unpublished_prekey()) {
       return;
     }
 
     const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } =
-      getAccountPrekeysSet(notificationAccount);
+      getAccountPrekeysSet(notifsCryptoAccount.notificationAccount);
     const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } =
       getAccountPrekeysSet(contentAccount);
 
@@ -910,9 +957,9 @@
       notifPrekeySignature,
     });
     contentAccount.mark_prekey_as_published();
-    notificationAccount.mark_prekey_as_published();
+    notifsCryptoAccount.notificationAccount.mark_prekey_as_published();
 
-    persistCryptoStore();
+    await persistCryptoStore(notifsCryptoAccount);
   },
   async signMessage(message: string): Promise<string> {
     if (!cryptoStore) {
@@ -945,12 +992,13 @@
     if (!cryptoStore) {
       throw new Error('Crypto account not initialized');
     }
-    const { contentAccount, notificationAccount } = cryptoStore;
+    const { contentAccount } = cryptoStore;
+    const notifsCryptoAccount = await getNotifsCryptoAccount();
 
     contentAccount.mark_prekey_as_published();
-    notificationAccount.mark_prekey_as_published();
+    notifsCryptoAccount.notificationAccount.mark_prekey_as_published();
 
-    persistCryptoStore();
+    await persistCryptoStore(notifsCryptoAccount);
   },
 };