diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3f3a2fd..33f24a8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -64,11 +64,15 @@ jobs:
         run: |
           set -ex
           export PATH=$(readlink -f ./gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin):$PATH
-          cd ./circle-stdlib/libs/circle/boot
-          make
-          make armstub64
-          cd -
-          cp -r ./circle-stdlib/libs/circle/boot/* sdcard
+          # cd ./circle-stdlib/libs/circle/boot
+          # make
+          # make armstub64
+          # cd -
+          # cp -r ./circle-stdlib/libs/circle/boot/* sdcard
+          cd ./sdcard
+          wget https://github.com/probonopd/MiniDexed/releases/download/assets/boot.zip
+          unzip boot.zip && rm boot.zip
+          cd ..
           rm -rf sdcard/config*.txt sdcard/README sdcard/Makefile sdcard/armstub sdcard/COPYING.linux
           cp ./src/config.txt ./src/minidexed.ini ./src/performance.ini sdcard/
           cp ./getsysex.sh sdcard/
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index f7c341d..d63666c 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -1123,6 +1123,7 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT
 	case TGParameterATAmplitude:				setModController(3, 2, nValue, nTG); break;
 	case TGParameterATEGBias:					setModController(3, 3, nValue, nTG); break;
 	
+	
 	case TGParameterMIDIChannel:
 		assert (0 <= nValue && nValue <= 255);
 		SetMIDIChannel ((uint8_t) nValue, nTG);
@@ -2306,7 +2307,7 @@ void CMiniDexed::UpdateNetwork()
 		}
 
 		if (m_pConfig->GetNetworkFTPEnabled()) {
-			m_pFTPDaemon = new CFTPDaemon(FTPUSERNAME, FTPPASSWORD);
+			m_pFTPDaemon = new CFTPDaemon(FTPUSERNAME, FTPPASSWORD, m_pmDNSPublisher, m_pConfig);
 
 			if (!m_pFTPDaemon->Initialize())
 			{
diff --git a/src/net/applemidi.cpp b/src/net/applemidi.cpp
index 8440426..8cbd3a4 100644
--- a/src/net/applemidi.cpp
+++ b/src/net/applemidi.cpp
@@ -460,7 +460,7 @@ bool ParseMIDIPacket(const u8* pBuffer, size_t nSize, TRTPMIDI* pOutPacket, CApp
 	return ParseMIDICommandSection(pMIDICommandSection, nRemaining, pHandler);
 }
 
-CAppleMIDIParticipant::CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, CAppleMIDIHandler* pHandler)
+CAppleMIDIParticipant::CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, CAppleMIDIHandler* pHandler, const char* pSessionName)
 	: CTask(TASK_STACK_SIZE, true),
 
 	  m_pRandom(pRandom),
@@ -492,7 +492,9 @@ CAppleMIDIParticipant::CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom,
 
 	  m_nSequence(0),
 	  m_nLastFeedbackSequence(0),
-	  m_nLastFeedbackTime(0)
+	  m_nLastFeedbackTime(0),
+
+      m_pSessionName(pSessionName)
 {
 }
 
@@ -805,8 +807,11 @@ bool CAppleMIDIParticipant::SendAcceptInvitationPacket(CSocket* pSocket, CIPAddr
 		{'\0'}
 	};
 
-	// TODO: configurable name
-	strncpy(AcceptPacket.Name, "MiniDexed", sizeof(AcceptPacket.Name));
+	// Use hostname as the session name
+	if (m_pSessionName && m_pSessionName[0])
+		strncpy(AcceptPacket.Name, m_pSessionName, sizeof(AcceptPacket.Name));
+	else
+		strncpy(AcceptPacket.Name, "MiniDexed", sizeof(AcceptPacket.Name));
 
 #ifdef APPLEMIDI_DEBUG
 	LOGNOTE("--> Accept invitation");
diff --git a/src/net/applemidi.h b/src/net/applemidi.h
index bc8d2b8..492be10 100644
--- a/src/net/applemidi.h
+++ b/src/net/applemidi.h
@@ -39,7 +39,7 @@ public:
 class CAppleMIDIParticipant : protected CTask
 {
 public:
-	CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, CAppleMIDIHandler* pHandler);
+	CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, CAppleMIDIHandler* pHandler, const char* pSessionName);
 	virtual ~CAppleMIDIParticipant() override;
 
 	bool Initialize();
@@ -109,6 +109,8 @@ private:
 	u16 m_nSequence = 0;
 	u16 m_nLastFeedbackSequence = 0;
 	u64 m_nLastFeedbackTime = 0;
+
+	const char* m_pSessionName;
 };
 
 #endif
\ No newline at end of file
diff --git a/src/net/ftpdaemon.cpp b/src/net/ftpdaemon.cpp
index 0cab51c..015dbc9 100644
--- a/src/net/ftpdaemon.cpp
+++ b/src/net/ftpdaemon.cpp
@@ -34,11 +34,13 @@ LOGMODULE("ftpd");
 constexpr u16 ListenPort = 21;
 constexpr u8 MaxConnections = 1;
 
-CFTPDaemon::CFTPDaemon(const char* pUser, const char* pPassword)
+CFTPDaemon::CFTPDaemon(const char* pUser, const char* pPassword, CmDNSPublisher* pMDNSPublisher, CConfig* pConfig)
 	: CTask(TASK_STACK_SIZE, true),
 	  m_pListenSocket(nullptr),
 	  m_pUser(pUser),
-	  m_pPassword(pPassword)
+	  m_pPassword(pPassword),
+	  m_pmDNSPublisher(pMDNSPublisher),
+	  m_pConfig(pConfig)
 {
 }
 
@@ -106,6 +108,6 @@ void CFTPDaemon::Run()
 		}
 
 		// Spawn new worker
-		new CFTPWorker(pConnection, m_pUser, m_pPassword);
+		new CFTPWorker(pConnection, m_pUser, m_pPassword, m_pmDNSPublisher, m_pConfig);
 	}
 }
\ No newline at end of file
diff --git a/src/net/ftpdaemon.h b/src/net/ftpdaemon.h
index 4d75762..77afb14 100644
--- a/src/net/ftpdaemon.h
+++ b/src/net/ftpdaemon.h
@@ -25,11 +25,13 @@
 
 #include <circle/net/socket.h>
 #include <circle/sched/task.h>
+#include "mdnspublisher.h"
+#include "../config.h"
 
 class CFTPDaemon : protected CTask
 {
 public:
-	CFTPDaemon(const char* pUser, const char* pPassword);
+	CFTPDaemon(const char* pUser, const char* pPassword, CmDNSPublisher* pMDNSPublisher, CConfig* pConfig);
 	virtual ~CFTPDaemon() override;
 
 	bool Initialize();
@@ -42,6 +44,8 @@ private:
 
 	const char* m_pUser;
 	const char* m_pPassword;
+	CmDNSPublisher* m_pmDNSPublisher;
+	CConfig* m_pConfig;
 };
 
 #endif
\ No newline at end of file
diff --git a/src/net/ftpworker.cpp b/src/net/ftpworker.cpp
index 28415e2..a19cfde 100644
--- a/src/net/ftpworker.cpp
+++ b/src/net/ftpworker.cpp
@@ -126,7 +126,7 @@ inline bool DirectoryCaseInsensitiveAscending(const TDirectoryListEntry& EntryA,
 }
 
 
-CFTPWorker::CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword)
+CFTPWorker::CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword, CmDNSPublisher* pMDNSPublisher, CConfig* pConfig)
 	: CTask(TASK_STACK_SIZE),
 	  m_LogName(),
 	  m_pExpectedUser(pExpectedUser),
@@ -142,7 +142,9 @@ CFTPWorker::CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const
 	  m_DataType(TDataType::ASCII),
 	  m_TransferMode(TTransferMode::Active),
 	  m_CurrentPath(),
-	  m_RenameFrom()
+	  m_RenameFrom(),
+	  m_pmDNSPublisher(pMDNSPublisher),
+	  m_pConfig(pConfig)
 {
 	++s_nInstanceCount;
 	m_LogName.Format("ftpd[%d]", s_nInstanceCount);
@@ -562,7 +564,7 @@ bool CFTPWorker::Passive(const char* pArgs)
 		IPAddress[2],
 		IPAddress[3],
 		(m_nDataSocketPort >> 8) & 0xFF,
-		m_nDataSocketPort & 0xFF
+		(m_nDataSocketPort & 0xFF)
 	);
 
 	SendStatus(TFTPStatus::EnteringPassiveMode, Buffer);
@@ -1058,9 +1060,24 @@ bool CFTPWorker::Bye(const char* pArgs)
 	SendStatus(TFTPStatus::ClosingControl, "Goodbye.");
 	delete m_pControlSocket;
 	m_pControlSocket = nullptr;
-	
+
+	// Unpublish the mDNS services
+	if (m_pmDNSPublisher && m_pConfig)
+	{
+		m_pmDNSPublisher->UnpublishService(m_pConfig->GetNetworkHostname());
+		m_pmDNSPublisher->UnpublishService(m_pConfig->GetNetworkHostname(), CmDNSPublisher::ServiceTypeAppleMIDI, 5004);
+		m_pmDNSPublisher->UnpublishService(m_pConfig->GetNetworkHostname(), CmDNSPublisher::ServiceTypeFTP, 21);
+	}
+
+	// Non-blocking 2 second delay before reboot
+	CTimer* const pTimer = CTimer::Get();
+	unsigned int start = pTimer->GetTicks();
+	while ((pTimer->GetTicks() - start) < 2 * HZ) {
+		CScheduler::Get()->Yield();
+	}
+
 	// Reboot the system if the user disconnects in order to apply any changes made
-	reboot ();
+	reboot();
 	return true;
 }
 
diff --git a/src/net/ftpworker.h b/src/net/ftpworker.h
index 62e60ed..17abfda 100644
--- a/src/net/ftpworker.h
+++ b/src/net/ftpworker.h
@@ -27,6 +27,8 @@
 #include <circle/net/socket.h>
 #include <circle/sched/task.h>
 #include <circle/string.h>
+#include "../config.h"
+#include "mdnspublisher.h"
 
 // TODO: These may be incomplete/inaccurate
 enum TFTPStatus
@@ -79,7 +81,7 @@ struct TDirectoryListEntry;
 class CFTPWorker : protected CTask
 {
 public:
-	CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword);
+	CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword, CmDNSPublisher* pMDNSPublisher, CConfig* pConfig);
 	virtual ~CFTPWorker() override;
 
 	virtual void Run() override;
@@ -142,6 +144,9 @@ private:
 	CString m_CurrentPath;
 	CString m_RenameFrom;
 
+	CmDNSPublisher* m_pmDNSPublisher;
+	CConfig* m_pConfig;
+
 	static void FatFsPathToFTPPath(const char* pInBuffer, char* pOutBuffer, size_t nSize);
 	static void FTPPathToFatFsPath(const char* pInBuffer, char* pOutBuffer, size_t nSize);
 
diff --git a/src/net/mdnspublisher.cpp b/src/net/mdnspublisher.cpp
index fbeb549..49ae79b 100644
--- a/src/net/mdnspublisher.cpp
+++ b/src/net/mdnspublisher.cpp
@@ -131,6 +131,45 @@ boolean CmDNSPublisher::UnpublishService (const char *pServiceName)
 	delete pService;
 	return TRUE;
 }
+boolean CmDNSPublisher::UnpublishService(const char *pServiceName, const char *pServiceType, u16 usServicePort)
+{
+	if (!m_bRunning)
+	{
+		return FALSE;
+	}
+	assert(pServiceName);
+	assert(pServiceType);
+	m_Mutex.Acquire();
+	TService *pService = nullptr;
+	TPtrListElement *pElement = m_ServiceList.GetFirst();
+	while (pElement)
+	{
+		pService = static_cast<TService *>(CPtrList::GetPtr(pElement));
+		assert(pService);
+		if (pService->ServiceName.Compare(pServiceName) == 0 &&
+		    pService->ServiceType.Compare(pServiceType) == 0 &&
+		    pService->usServicePort == usServicePort)
+		{
+			m_ServiceList.Remove(pElement);
+			break;
+		}
+		pService = nullptr;
+		pElement = m_ServiceList.GetNext(pElement);
+	}
+	m_Mutex.Release();
+	if (!pService)
+	{
+		return FALSE;
+	}
+	LOGDBG("Unpublish service %s %s %u", (const char *)pService->ServiceName, (const char *)pService->ServiceType, pService->usServicePort);
+	SendResponse(pService, FALSE);
+	for (unsigned i = 0; i < pService->nTextRecords; i++)
+	{
+		delete pService->ppText[i];
+	}
+	delete pService;
+	return TRUE;
+}
 void CmDNSPublisher::Run (void)
 {
 	assert (m_pNet);
diff --git a/src/net/mdnspublisher.h b/src/net/mdnspublisher.h
index 6b132a7..c26f6ea 100644
--- a/src/net/mdnspublisher.h
+++ b/src/net/mdnspublisher.h
@@ -32,6 +32,7 @@ class CmDNSPublisher : public CTask	/// mDNS / Bonjour client task
 {
 public:
 	static constexpr const char *ServiceTypeAppleMIDI = "_apple-midi._udp";
+	static constexpr const char *ServiceTypeFTP = "_ftp._tcp";
 public:
 	/// \param pNet Pointer to the network subsystem object
 	CmDNSPublisher (CNetSubSystem *pNet);
@@ -50,6 +51,12 @@ public:
 	/// \param pServiceName Name of the service to be unpublished (same as when published)
 	/// \return Operation successful?
 	boolean UnpublishService (const char *pServiceName);
+	/// \brief Stop publishing a service
+	/// \param pServiceName Name of the service to be unpublished
+	/// \param pServiceType Type of the service to be unpublished
+	/// \param usServicePort Port number of the service to be unpublished
+	/// \return Operation successful?
+	boolean UnpublishService (const char *pServiceName, const char *pServiceType, u16 usServicePort);
 	void Run (void) override;
 private:
 	static const unsigned MaxTextRecords = 10;
diff --git a/src/performanceconfig.cpp b/src/performanceconfig.cpp
index d0ce2de..dfff8a9 100644
--- a/src/performanceconfig.cpp
+++ b/src/performanceconfig.cpp
@@ -33,7 +33,6 @@ LOGMODULE ("Performance");
 #define PERFORMANCE_DIR "performance" 
 #define DEFAULT_PERFORMANCE_FILENAME "performance.ini"
 #define DEFAULT_PERFORMANCE_NAME "Default"
-#define DEFAULT_PERFORMANCE_BANK_NAME "Default"
 
 CPerformanceConfig::CPerformanceConfig (FATFS *pFileSystem)
 :	m_Properties (DEFAULT_PERFORMANCE_FILENAME, pFileSystem)
@@ -1168,10 +1167,6 @@ bool CPerformanceConfig::ListPerformanceBanks()
 	}
 
 	unsigned nNumBanks = 0;
-	
-	// Add in the default performance directory as the first bank
-	m_PerformanceBankName[0] = DEFAULT_PERFORMANCE_BANK_NAME;
-	nNumBanks = 1;
 	m_nLastPerformanceBank = 0;
 
 	// List directories with names in format 01_Perf Bank Name
@@ -1277,10 +1272,7 @@ std::string CPerformanceConfig::GetPerformanceBankName(unsigned nBankID)
 	{
 		return m_PerformanceBankName[nBankID];
 	}
-	else
-	{
-		return DEFAULT_PERFORMANCE_BANK_NAME;
-	}
+	return "";
 }
 
 std::string CPerformanceConfig::AddPerformanceBankDirName(unsigned nBankID)
@@ -1290,12 +1282,6 @@ std::string CPerformanceConfig::AddPerformanceBankDirName(unsigned nBankID)
 	{
 		// Performance Banks directories in format "001_Bank Name"
 		std::string Index;
-		if (nBankID == 0)
-		{
-			// Legacy: Bank 1 is the default performance directory
-			return "";
-		}
-
 		if (nBankID < 9)
 		{
 			Index = "00" + std::to_string(nBankID+1);
diff --git a/src/udpmididevice.cpp b/src/udpmididevice.cpp
index 4c46649..000bc3d 100644
--- a/src/udpmididevice.cpp
+++ b/src/udpmididevice.cpp
@@ -48,7 +48,7 @@ CUDPMIDIDevice::~CUDPMIDIDevice (void)
 
 boolean CUDPMIDIDevice::Initialize (void)
 {
-	m_pAppleMIDIParticipant = new CAppleMIDIParticipant(&m_Random, this);
+	m_pAppleMIDIParticipant = new CAppleMIDIParticipant(&m_Random, this, m_pConfig->GetNetworkHostname());
 	if (!m_pAppleMIDIParticipant->Initialize())
 	{
 		LOGERR("Failed to init RTP listener");
diff --git a/submod.sh b/submod.sh
index 6ef767a..32408ef 100755
--- a/submod.sh
+++ b/submod.sh
@@ -23,5 +23,5 @@ cd -
 # Use fixed master branch of Synth_Dexed
 cd Synth_Dexed/
 git reset --hard
-git checkout 65d8383ad5 -f
+git checkout 2ad9e43095 -f
 cd -
diff --git a/syslogserver.py b/syslogserver.py
index feffaa6..db45e7f 100644
--- a/syslogserver.py
+++ b/syslogserver.py
@@ -15,6 +15,7 @@ class SyslogServer:
         self.port = port
         self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         self.server.bind((self.host, self.port))
+        self.server.settimeout(0.5)  # Set timeout to allow checking self.running
         self.start_time = None
         self.running = True
 
@@ -28,12 +29,17 @@ class SyslogServer:
             try:
                 data, address = self.server.recvfrom(1024)
                 self.handle_message(data)
+            except socket.timeout:
+                continue  # Check self.running again
             except KeyboardInterrupt:
                 self.running = False
 
     def handle_message(self, data):
         message = data[2:].decode('utf-8').strip()
 
+        if "Time exceeded (0)" in message:
+            return
+
         if self.start_time is None:
             self.start_time = time.time()
             relative_time = "0:00:00.000"
@@ -57,4 +63,4 @@ class SyslogServer:
 if __name__ == "__main__":
     server = SyslogServer()
     server.start()
-    print("Syslog server stopped.")
\ No newline at end of file
+    print("Syslog server stopped.")
diff --git a/updater.py b/updater.py
index 30e6749..bc33058 100644
--- a/updater.py
+++ b/updater.py
@@ -345,13 +345,18 @@ if __name__ == "__main__":
                         print(f"\nUploaded {file} to {selected_ip}.")
     except ftplib.all_errors as e:
         print(f"FTP error: {e}")
+        ftp = None  # Mark ftp as unusable
 
+    # Only attempt to send BYE if ftp is connected and has a socket
     try:
-        ftp.sendcmd("BYE")
-        print(f"Disconnected from {selected_ip}.")
-    except ftplib.all_errors as e:
-        if str(e).strip().lower() == "timed out":
-            # Suppress expected timeout after BYE
+        if ftp is not None and getattr(ftp, 'sock', None) is not None:
+            ftp.sendcmd("BYE")
             print(f"Disconnected from {selected_ip}.")
+        else:
+            print(f"No active FTP connection to disconnect from.")
+    except (*ftplib.all_errors, AttributeError) as e:
+        # Handle timeout or already closed connection
+        if isinstance(e, TimeoutError) or (str(e).strip().lower() == "timed out"):
+            print(f"Disconnected from {selected_ip} (timeout after BYE, device likely restarted).")
         else:
             print(f"FTP error after BYE: {e}")