diff --git a/Documentation/card10-cfg.rst b/Documentation/card10-cfg.rst
index 881c3bd1c1a9b2bb8f61db6c1c536e1f96f8ba50..7a0173b3daceb49d8481e09948c485c1cec6a628 100644
--- a/Documentation/card10-cfg.rst
+++ b/Documentation/card10-cfg.rst
@@ -43,5 +43,9 @@ Option name        Type       Description
 ------------------ ---------- -----------
 ``default_app``    String     Full path to the exectutable file of the default application. If this option is not set,``apps/analog_clock/__init__.py`` is used.
 ------------------ ---------- -----------
+``ble_enable``     Boolean    Activate the BLE interface. Turn off for more privacy or to conserve energy.
+------------------ ---------- -----------
+``ble_mac``        Boolean    MAC address used for BLE. Format: ``ca:4d:10:xx:xx:xx``.
+------------------ ---------- -----------
 ``ble_log_enable`` Boolean    Activate HCI level logging of BLE data. Creates a new btsnoop compatible log file named ``ble.log`` in the ``logs`` folder after each boot if BLE is activated. Keeps the last 10 files.
 ================== ========== ===========
diff --git a/epicardium/ble/ble.c b/epicardium/ble/ble.c
index 120bd31fa6bb0e57f30b5783a645aceb963feef5..b3e175f1dd1c6f5dea54b5d9bf2ae430c53386de 100644
--- a/epicardium/ble/ble.c
+++ b/epicardium/ble/ble.c
@@ -180,11 +180,11 @@ static void setAddress(void)
 	uint8_t bdAddr[6] = { 0xCA, 0x4D, 0x10, 0x00, 0x00, 0x00 };
 	char buf[32];
 
-	int result = fs_read_text_file("mac.txt", buf, sizeof(buf));
+	int result = epic_config_get_string("ble_mac", buf, sizeof(buf));
 
 	if (result < 0) {
-		APP_TRACE_INFO0("mac.txt not found, generating random MAC");
-		epic_trng_read(bdAddr + 3, 3);
+		APP_TRACE_INFO0("ble_mac not set. Generating random MAC");
+		epic_csprng_read(bdAddr + 3, 3);
 		sprintf(buf,
 			"%02x:%02x:%02x:%02x:%02x:%02x\n",
 			bdAddr[0],
@@ -193,9 +193,9 @@ static void setAddress(void)
 			bdAddr[3],
 			bdAddr[4],
 			bdAddr[5]);
-		fs_write_file("mac.txt", buf, strlen(buf));
+		epic_config_set_string("ble_mac", buf);
 	} else {
-		APP_TRACE_INFO1("mac file contents: %s", buf);
+		APP_TRACE_INFO1("ble_mac: %s", buf);
 	}
 
 	int a, b, c, d, e, f;
@@ -261,35 +261,17 @@ void RSV11_IRQHandler(void)
 	notify();
 }
 /*************************************************************************************************/
-#define BLEMAXCFGBYTES 100
 bool ble_shall_start(void)
 {
-	int bleConfigFile = epic_file_open("ble.txt", "r");
-	if (bleConfigFile < 0) {
-		LOG_INFO("ble", "can not open ble.txt -> BLE is not started");
-		epic_file_close(bleConfigFile);
-		return false;
-	}
-
-	char cfgBuf[BLEMAXCFGBYTES + 1];
-	int readNum = epic_file_read(bleConfigFile, cfgBuf, BLEMAXCFGBYTES);
-	epic_file_close(bleConfigFile);
-	if (readNum < 0) {
-		LOG_WARN("ble", "can not read ble.txt -> BLE is not started");
-		return false;
-	}
-	cfgBuf[readNum] = '\0';
+	bool ble_enabled = config_get_boolean_with_default("ble_enable", false);
 
-	char bleActiveStr[]              = "active=true";
-	cfgBuf[sizeof(bleActiveStr) - 1] = '\0';
-
-	if (strcmp(cfgBuf, "active=true") != 0) {
-		LOG_INFO("ble", "BLE is disabled.");
-		return false;
-	} else {
+	if (ble_enabled) {
 		LOG_INFO("ble", "BLE is enabled.");
-		return true;
+	} else {
+		LOG_INFO("ble", "BLE is disabled.");
 	}
+
+	return ble_enabled;
 }
 /*************************************************************************************************/
 static void scheduleTimer(void)
diff --git a/epicardium/ble/ble_main.c b/epicardium/ble/ble_main.c
index a5522b4e741413cc2af4d239ac96d9dd8fa5c7a2..dc3d54669c2619f1cf548a7310c1e379f2a8dd34 100644
--- a/epicardium/ble/ble_main.c
+++ b/epicardium/ble/ble_main.c
@@ -394,6 +394,7 @@ static void bleCccCback(attsCccEvt_t *pEvt)
   {
     /* store value in device database */
     AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
+    AppDbNvmStoreCccTbl(dbHdl);
   }
 
   if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
@@ -463,7 +464,8 @@ static void bleSetup(bleMsg_t *pMsg)
   char buf[32];
   char a, b, c, d, e, f, K;
 
-  if (fs_read_text_file("mac.txt", buf, sizeof(buf)) > 0)
+  int result = epic_config_get_string("ble_mac", buf, sizeof(buf));
+  if (result == 0)
   {
     if (sscanf(buf, "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c", &K,&K,&K,&K,&K,&K, &a, &b, &c, &d, &e, &f) == 12)
     {
@@ -761,6 +763,10 @@ static void bleProcMsg(bleMsg_t *pMsg)
     case DM_SEC_PAIR_CMPL_IND:
       LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X",
                pMsg->dm.pairCmpl.auth);
+
+      DmSecGenerateEccKeyReq();
+      AppDbNvmStoreBond(AppDbGetHdl((dmConnId_t) pMsg->hdr.param));
+
       pair_connId = DM_CONN_ID_NONE;
       trigger_event(BLE_EVENT_PAIRING_COMPLETE);
       /* After a successful pairing, bonding is disabled again.
@@ -783,6 +789,9 @@ static void bleProcMsg(bleMsg_t *pMsg)
                    pMsg->hdr.status);
           break;
       }
+
+      DmSecGenerateEccKeyReq();
+
       pair_connId = DM_CONN_ID_NONE;
       trigger_event(BLE_EVENT_PAIRING_FAILED);
       break;
diff --git a/epicardium/ble/app/common/app_db.c b/epicardium/ble/bondings.c
similarity index 73%
rename from epicardium/ble/app/common/app_db.c
rename to epicardium/ble/bondings.c
index 550077784b4b120f430c8933c4a075660aba438e..42a06f133841384477c273a7c58b17c2db98655e 100644
--- a/epicardium/ble/app/common/app_db.c
+++ b/epicardium/ble/bondings.c
@@ -20,7 +20,7 @@
 /* card10:
  * copied from: lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/apps/app/common/app_db.c
  *
- * Reason: we need to implement persistent storage for pairings
+ * Reason: we need to implement persistent storage for bondings
  */
 /* clang-format off */
 /* clang-formet turned off for easier diffing against orginal file */
@@ -41,6 +41,35 @@
 #include <string.h>
 #include <stdio.h>
 
+/**************************************************************************************************
+  Macros
+**************************************************************************************************/
+
+/* App DB NVM record parameter indicies from upstream:
+ * https://github.com/packetcraft-inc/stacks/blob/master/ble-profiles/sources/af/common/app_db.c#L46
+ */
+#define APP_DB_NVM_IN_USE_ID                  0
+#define APP_DB_NVM_PEER_ADDR_ID               1
+#define APP_DB_NVM_ADDR_TYPE_ID               2
+#define APP_DB_NVM_PEER_IRK_ID                3
+#define APP_DB_NVM_PEER_CSRK_ID               4
+#define APP_DB_NVM_KV_MASK_ID                 5
+#define APP_DB_NVM_VALID_ID                   6
+#define APP_DB_NVM_PEER_RAPO_ID               7
+#define APP_DB_NVM_LOCAL_LTK_ID               8
+#define APP_DB_NVM_LOCAL_SEC_LVL_ID           9
+#define APP_DB_NVM_PEER_ADDR_RES_ID           10
+#define APP_DB_NVM_PEER_LTK_ID                11
+#define APP_DB_NVM_PEER_SEC_LVL_ID            12
+#define APP_DB_NVM_CCC_TBL_ID                 13
+#define APP_DB_NVM_PEER_SIGN_CTR_ID           14
+#define APP_DB_NVM_CAS_ID                     15
+#define APP_DB_NVM_CSF_ID                     16
+#define APP_DB_NVM_CACHE_HASH_ID              17
+#define APP_DB_NVM_HASH_ID                    18
+#define APP_DB_NVM_HDL_LIST_ID                19
+#define APP_DB_NVM_DISC_STATUS_ID             20
+
 /**************************************************************************************************
   Data Types
 **************************************************************************************************/
@@ -77,23 +106,179 @@ typedef struct
   uint8_t     discStatus;                   /*! Service discovery and configuration status */
 } appDbRec_t;
 
-/*! Database type */
-typedef struct
-{
-  appDbRec_t  rec[APP_DB_NUM_RECS];               /*! Device database records */
-  char        devName[ATT_DEFAULT_PAYLOAD_LEN];   /*! Device name */
-  uint8_t     devNameLen;                         /*! Device name length */
-} appDb_t;
 
 /**************************************************************************************************
   Local Variables
 **************************************************************************************************/
 
 /*! Database */
-static appDb_t appDb;
+static appDbRec_t records[APP_DB_NUM_RECS];
 
 /*! When all records are allocated use this index to determine which to overwrite */
-static appDbRec_t *pAppDbNewRec = appDb.rec;
+static appDbRec_t *pAppDbNewRec = records;
+
+/* clang-format on */
+/* Translate a pointer to a record into the filename to be used for it. */
+static int record_to_filename(appDbRec_t *record, char *buf, size_t buf_size)
+{
+	int id  = record - records;
+	int ret = snprintf(buf, buf_size, "pairings/pairing%d.bin", id + 1);
+	if (ret >= (int)buf_size) {
+		ret = -1;
+	}
+	return ret;
+}
+
+/* Write a TLV to a file. */
+static int write_tlv(int fd, uint32_t t, uint32_t l, void *v)
+{
+	int ret;
+
+	ret = epic_file_write(fd, &t, sizeof(t));
+	if (ret != sizeof(t))
+		return ret;
+
+	ret = epic_file_write(fd, &l, sizeof(l));
+	if (ret != sizeof(l))
+		return ret;
+
+	ret = epic_file_write(fd, v, l);
+	if (ret != (int)l)
+		return ret;
+
+	return 0;
+}
+
+/* Read a TLV from a file.
+ *
+ * Super naive implementation assuming that the next TLV is
+ * the expected one. */
+static int read_tlv(int fd, uint32_t t, uint32_t l, void *v)
+{
+	int ret;
+
+	uint32_t t_r;
+	ret = epic_file_read(fd, &t_r, sizeof(t_r));
+	if (ret != sizeof(t))
+		return ret;
+	if (t != t_r)
+		return -ENOENT;
+
+	uint32_t l_r;
+	ret = epic_file_read(fd, &l_r, sizeof(l_r));
+	if (ret != sizeof(l_r))
+		return ret;
+	if (l_r > l)
+		return -EINVAL;
+
+	memset(v, 0, l);
+
+	ret = epic_file_read(fd, v, l_r);
+	if (ret != (int)l_r)
+		return ret;
+
+	return 0;
+}
+
+static int write_bond_to_file(appDbRec_t *r, char *filename)
+{
+	if (!r->inUse) {
+		return -EINVAL;
+	}
+
+	int fd = epic_file_open(filename, "w");
+	int ret;
+	if (fd < 0) {
+		return fd;
+	}
+
+	static const uint8_t version = 1;
+	ret = epic_file_write(fd, &version, sizeof(version));
+	if (ret != sizeof(version))
+		goto out;
+
+#define write_element(t, x)                                                    \
+	if ((ret = write_tlv(fd, t, sizeof(r->x), &r->x)))                     \
+		goto out;
+
+	write_element(APP_DB_NVM_PEER_ADDR_ID, peerAddr);
+	write_element(APP_DB_NVM_ADDR_TYPE_ID, addrType);
+	write_element(APP_DB_NVM_PEER_IRK_ID, peerIrk);
+	write_element(APP_DB_NVM_PEER_CSRK_ID, peerCsrk);
+	write_element(APP_DB_NVM_KV_MASK_ID, keyValidMask);
+	/* peerAddedToRl not persisted by upstream */
+	/* write_element(, peerAddedToRl); */
+	write_element(APP_DB_NVM_PEER_RAPO_ID, peerRpao);
+	write_element(APP_DB_NVM_LOCAL_LTK_ID, localLtk);
+	write_element(APP_DB_NVM_LOCAL_SEC_LVL_ID, localLtkSecLevel);
+	write_element(APP_DB_NVM_PEER_ADDR_RES_ID, peerAddrRes);
+	write_element(APP_DB_NVM_PEER_LTK_ID, peerLtk);
+	write_element(APP_DB_NVM_PEER_SEC_LVL_ID, peerLtkSecLevel);
+	write_element(APP_DB_NVM_CCC_TBL_ID, cccTbl);
+	write_element(APP_DB_NVM_PEER_SIGN_CTR_ID, peerSignCounter);
+	write_element(APP_DB_NVM_HDL_LIST_ID, hdlList);
+	write_element(APP_DB_NVM_DISC_STATUS_ID, discStatus);
+	write_element(APP_DB_NVM_VALID_ID, valid);
+
+#undef write_element
+
+out:
+	epic_file_close(fd);
+	return ret;
+}
+
+static int read_bond_from_file(appDbRec_t *r, char *filename)
+{
+	int fd = epic_file_open(filename, "r");
+	if (fd < 0) {
+		return fd;
+	}
+
+	uint8_t version;
+	int ret = epic_file_read(fd, &version, sizeof(version));
+	if (ret != sizeof(version)) {
+		goto out;
+	}
+	if (version != 1) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+#define read_element(t, x)                                                     \
+	if ((ret = read_tlv(fd, t, sizeof(r->x), &r->x)))                      \
+		goto out;
+
+	read_element(APP_DB_NVM_PEER_ADDR_ID, peerAddr);
+	read_element(APP_DB_NVM_ADDR_TYPE_ID, addrType);
+	read_element(APP_DB_NVM_PEER_IRK_ID, peerIrk);
+	read_element(APP_DB_NVM_PEER_CSRK_ID, peerCsrk);
+	read_element(APP_DB_NVM_KV_MASK_ID, keyValidMask);
+	/* peerAddedToRl not persisted by upstream */
+	/* read_element(, peerAddedToRl); */
+	read_element(APP_DB_NVM_PEER_RAPO_ID, peerRpao);
+	read_element(APP_DB_NVM_LOCAL_LTK_ID, localLtk);
+	read_element(APP_DB_NVM_LOCAL_SEC_LVL_ID, localLtkSecLevel);
+	read_element(APP_DB_NVM_PEER_ADDR_RES_ID, peerAddrRes);
+	read_element(APP_DB_NVM_PEER_LTK_ID, peerLtk);
+	read_element(APP_DB_NVM_PEER_SEC_LVL_ID, peerLtkSecLevel);
+	read_element(APP_DB_NVM_CCC_TBL_ID, cccTbl);
+	read_element(APP_DB_NVM_PEER_SIGN_CTR_ID, peerSignCounter);
+	read_element(APP_DB_NVM_HDL_LIST_ID, hdlList);
+	read_element(APP_DB_NVM_DISC_STATUS_ID, discStatus);
+	read_element(APP_DB_NVM_VALID_ID, valid);
+
+#undef read_element
+
+	r->inUse = true;
+out:
+	epic_file_close(fd);
+	return ret;
+}
+
+static int delete_bond(char *filename)
+{
+	return epic_file_unlink(filename);
+}
 
 /*************************************************************************************************/
 /*!
@@ -104,28 +289,27 @@ static appDbRec_t *pAppDbNewRec = appDb.rec;
 /*************************************************************************************************/
 void AppDbInit(void)
 {
-  int fd = epic_file_open("pairings.bin", "r");
-
-  if(fd >= 0) {
-    if(epic_file_read(fd, &appDb, sizeof(appDb)) != sizeof(appDb)) {
-        memset(&appDb, 0, sizeof(appDb));
-    }
-    epic_file_close(fd);
-  }
-}
-
-static void AppDbStore(void)
-{
-  LOG_INFO("appDb", "writing to persistent storage");
-
-  int fd = epic_file_open("pairings.bin", "w");
-  if(fd >= 0) {
-    if(epic_file_write(fd, &appDb, sizeof(appDb)) != sizeof(appDb)) {
-    }
-    epic_file_close(fd);
-  }
+	memset(&records, 0, sizeof(records));
+
+	char filename[32];
+	for (int i = 0; i < APP_DB_NUM_RECS; i++) {
+		record_to_filename(&records[i], filename, sizeof(filename));
+		int ret = read_bond_from_file(&records[i], filename);
+		if (ret < 0) {
+			if (ret != -ENOENT) {
+				LOG_WARN(
+					"bondings",
+					"Reading pairing '%s' failed: %d",
+					filename,
+					ret
+				);
+			}
+			memset(&records[i], 0, sizeof(records[i]));
+		}
+	}
 }
 
+/* clang-format off */
 /*************************************************************************************************/
 /*!
  *  \brief  Create a new device database record.
@@ -138,7 +322,7 @@ static void AppDbStore(void)
 /*************************************************************************************************/
 appDbHdl_t AppDbNewRecord(uint8_t addrType, uint8_t *pAddr)
 {
-  appDbRec_t  *pRec = appDb.rec;
+  appDbRec_t  *pRec = records;
   uint8_t     i;
 
   /* find a free record */
@@ -158,9 +342,9 @@ appDbHdl_t AppDbNewRecord(uint8_t addrType, uint8_t *pAddr)
 
     /* get next record to overwrite */
     pAppDbNewRec++;
-    if (pAppDbNewRec == &appDb.rec[APP_DB_NUM_RECS])
+    if (pAppDbNewRec == &records[APP_DB_NUM_RECS])
     {
-      pAppDbNewRec = appDb.rec;
+      pAppDbNewRec = records;
     }
   }
 
@@ -192,7 +376,7 @@ appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
   /* if first record is requested */
   if (hdl == APP_DB_HDL_NONE)
   {
-    pRec = appDb.rec;
+    pRec = records;
   }
   /* if valid record passed in */
   else if (AppDbRecordInUse(hdl))
@@ -207,7 +391,7 @@ appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
   }
 
   /* look for next valid record */
-  while (pRec < &appDb.rec[APP_DB_NUM_RECS])
+  while (pRec < &records[APP_DB_NUM_RECS])
   {
     /* if record is in use */
     if (pRec->inUse && pRec->valid)
@@ -236,6 +420,9 @@ appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
 void AppDbDeleteRecord(appDbHdl_t hdl)
 {
   ((appDbRec_t *) hdl)->inUse = FALSE;
+  char filename[32];
+  record_to_filename((appDbRec_t *) hdl, filename, sizeof(filename));
+  delete_bond(filename);
 }
 
 /*************************************************************************************************/
@@ -253,7 +440,6 @@ void AppDbValidateRecord(appDbHdl_t hdl, uint8_t keyMask)
 {
   ((appDbRec_t *) hdl)->valid = TRUE;
   ((appDbRec_t *) hdl)->keyValidMask = keyMask;
-  AppDbStore();
 }
 
 /*************************************************************************************************/
@@ -285,7 +471,7 @@ void AppDbCheckValidRecord(appDbHdl_t hdl)
 /*************************************************************************************************/
 bool_t AppDbRecordInUse(appDbHdl_t hdl)
 {
-  appDbRec_t  *pRec = appDb.rec;
+  appDbRec_t  *pRec = records;
   uint8_t     i;
 
   /* see if record is in database record list */
@@ -311,7 +497,7 @@ bool_t AppDbRecordInUse(appDbHdl_t hdl)
 /*************************************************************************************************/
 bool_t AppDbCheckBonded(void)
 {
-  appDbRec_t  *pRec = appDb.rec;
+  appDbRec_t  *pRec = records;
   uint8_t     i;
 
   /* find a record */
@@ -335,7 +521,7 @@ bool_t AppDbCheckBonded(void)
 /*************************************************************************************************/
 void AppDbDeleteAllRecords(void)
 {
-  appDbRec_t  *pRec = appDb.rec;
+  appDbRec_t  *pRec = records;
   uint8_t     i;
 
   /* set in use to false for all records */
@@ -357,7 +543,7 @@ void AppDbDeleteAllRecords(void)
 /*************************************************************************************************/
 appDbHdl_t AppDbFindByAddr(uint8_t addrType, uint8_t *pAddr)
 {
-  appDbRec_t  *pRec = appDb.rec;
+  appDbRec_t  *pRec = records;
   uint8_t     peerAddrType = DmHostAddrType(addrType);
   uint8_t     i;
 
@@ -385,7 +571,7 @@ appDbHdl_t AppDbFindByAddr(uint8_t addrType, uint8_t *pAddr)
 /*************************************************************************************************/
 appDbHdl_t AppDbFindByLtkReq(uint16_t encDiversifier, uint8_t *pRandNum)
 {
-  appDbRec_t  *pRec = appDb.rec;
+  appDbRec_t  *pRec = records;
   uint8_t     i;
 
   /* find matching record */
@@ -581,48 +767,6 @@ void AppDbSetHdlList(appDbHdl_t hdl, uint16_t *pHdlList)
   memcpy(((appDbRec_t *) hdl)->hdlList, pHdlList, sizeof(((appDbRec_t *) hdl)->hdlList));
 }
 
-/*************************************************************************************************/
-/*!
- *  \brief  Get the device name.
- *
- *  \param  pLen      Returned device name length.
- *
- *  \return Pointer to UTF-8 string containing device name or NULL if not set.
- */
-/*************************************************************************************************/
-char *AppDbGetDevName(uint8_t *pLen)
-{
-  /* if first character of name is NULL assume it is uninitialized */
-  if (appDb.devName[0] == 0)
-  {
-    *pLen = 0;
-    return NULL;
-  }
-  else
-  {
-    *pLen = appDb.devNameLen;
-    return appDb.devName;
-  }
-}
-
-/*************************************************************************************************/
-/*!
- *  \brief  Set the device name.
- *
- *  \param  len       Device name length.
- *  \param  pStr      UTF-8 string containing device name.
- *
- *  \return None.
- */
-/*************************************************************************************************/
-void AppDbSetDevName(uint8_t len, char *pStr)
-{
-  /* check for maximum device length */
-  len = (len <= sizeof(appDb.devName)) ? len : sizeof(appDb.devName);
-
-  memcpy(appDb.devName, pStr, len);
-}
-
 /*************************************************************************************************/
 /*!
  *  \brief  Get address resolution attribute value read from a peer device.
@@ -740,4 +884,49 @@ void AppDbSetPeerRpao(appDbHdl_t hdl, bool_t peerRpao)
 {
   ((appDbRec_t *)hdl)->peerRpao = peerRpao;
 }
+
+/*************************************************************************************************/
+/*!
+ *  \brief  Store the client characteristic configuration table for a device record in NVM.
+ *
+ *  \param  hdl       Database record handle.
+ *
+ *  \return None.
+ */
+/*************************************************************************************************/
+void AppDbNvmStoreCccTbl(appDbHdl_t hdl)
+{
+  /* We take a short cut and simply write the whole file again. */
+  AppDbNvmStoreBond(hdl);
+}
+
+/*************************************************************************************************/
+/*!
+ *  \brief  Store bonding information for device record in NVM.
+ *
+ *  \param  hdl       Database record handle.
+ *
+ *  \return None.
+ */
+/*************************************************************************************************/
+void AppDbNvmStoreBond(appDbHdl_t hdl)
+{
+  appDbRec_t  *pRec = (appDbRec_t *) hdl;
+
+  if (pRec->inUse && pRec->valid) {
+    char filename[32];
+    record_to_filename(pRec, filename, sizeof(filename));
+    /* Directory might exist already. Call will fail silently in that case. */
+    epic_file_mkdir("pairings");
+    int ret = write_bond_to_file(pRec, filename);
+    if(ret < 0) {
+		LOG_WARN(
+			"bondings",
+			"Writing pairing '%s' failed: %d",
+			filename,
+			ret
+		);
+	}
+  }
+}
 /* clang-format on */
diff --git a/epicardium/ble/meson.build b/epicardium/ble/meson.build
index 41280358b05f0d04c470049a6d841fbea0c92fe2..058022ec912a4eba4fdf1b0498d2cbb8ebac12a8 100644
--- a/epicardium/ble/meson.build
+++ b/epicardium/ble/meson.build
@@ -4,7 +4,7 @@ ble_sources = files(
   'ble_main.c',
   'svc_dis.c',
   'svc_core.c',
-  'app/common/app_db.c',
+  'bondings.c',
   'uart.c',
   'card10.c',
   'filetransfer.c',
diff --git a/lib/sdk/Libraries/BTLE/stack/ble-profiles/include/app/app_db.h b/lib/sdk/Libraries/BTLE/stack/ble-profiles/include/app/app_db.h
index e72fb4cf8d3b5ac912d66f70fa01c634546ae146..a34b6d3bbb1e20444d3892f34d4e20e915ddd4e4 100644
--- a/lib/sdk/Libraries/BTLE/stack/ble-profiles/include/app/app_db.h
+++ b/lib/sdk/Libraries/BTLE/stack/ble-profiles/include/app/app_db.h
@@ -395,6 +395,28 @@ bool_t AppDbGetPeerRpao(appDbHdl_t hdl);
 /*************************************************************************************************/
 void AppDbSetPeerRpao(appDbHdl_t hdl, bool_t peerRpao);
 
+/*************************************************************************************************/
+/*!
+ *  \brief  Store the client characteristic configuration table for a device record in NVM.
+ *
+ *  \param  hdl       Database record handle.
+ *
+ *  \return None.
+ */
+/*************************************************************************************************/
+void AppDbNvmStoreCccTbl(appDbHdl_t hdl);
+
+/*************************************************************************************************/
+/*!
+ *  \brief  Store bonding information for device record in NVM.
+ *
+ *  \param  hdl       Database record handle.
+ *
+ *  \return None.
+ */
+/*************************************************************************************************/
+void AppDbNvmStoreBond(appDbHdl_t hdl);
+
 /**@}*/
 
 /*! \} */    /*! APP_FRAMEWORK_DB_API */
diff --git a/preload/apps/ble/__init__.py b/preload/apps/ble/__init__.py
index d5b0b7163a0ef44664a587bce87d8acb47af0628..56d3857fe6a841034ad9f6eb2bbb0989644d6cdf 100644
--- a/preload/apps/ble/__init__.py
+++ b/preload/apps/ble/__init__.py
@@ -4,11 +4,8 @@ import time
 import buttons
 import sys_ble
 import interrupt
+import config
 
-CONFIG_NAME = "ble.txt"
-MAC_NAME = "mac.txt"
-ACTIVE_STRING = "active=true"
-INACTIVE_STRING = "active=false"
 ble_event = None
 
 
@@ -18,18 +15,16 @@ def ble_callback(_):
 
 
 def init():
-    if CONFIG_NAME not in os.listdir("."):
-        with open(CONFIG_NAME, "w") as f:
-            f.write(INACTIVE_STRING)
     interrupt.set_callback(interrupt.BLE, ble_callback)
     interrupt.enable_callback(interrupt.BLE)
     sys_ble.set_bondable(True)
 
 
 def load_mac():
-    if MAC_NAME in os.listdir("."):
-        with open(MAC_NAME) as f:
-            return f.read().strip()
+    try:
+        return config.get_string("ble_mac")
+    except OSError:
+        return None
 
 
 def triangle(disp, x, y, left):
@@ -41,9 +36,10 @@ def triangle(disp, x, y, left):
 
 
 def toggle():
-    content = INACTIVE_STRING if is_active() else ACTIVE_STRING
-    with open(CONFIG_NAME, "w") as f:
-        f.write(content)
+    if is_active():
+        config.set_string("ble_enable", "false")
+    else:
+        config.set_string("ble_enable", "true")
 
     disp.clear()
     disp.print("resetting", posy=0, fg=[0, 255, 255])
@@ -54,12 +50,14 @@ def toggle():
 
 
 def is_active():
-    with open(CONFIG_NAME, "r") as f:
-        state = f.readlines()[0]
-        if len(state) < len(ACTIVE_STRING):
-            return False
-        state = state[0 : len(ACTIVE_STRING)]
-        return state == ACTIVE_STRING
+    try:
+        active = config.get_string("ble_enable")
+        if active == "true" or active == "1":
+            return True
+    except OSError:
+        pass
+
+    return False
 
 
 def headline():