diff --git a/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt b/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9205a0e5203c2ed7c4308855c4250e9fdca8cce2
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt
@@ -0,0 +1,73 @@
+package org.briarproject.briar.desktop.expiration
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Warning
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.briarproject.briar.desktop.expiration.ExpirationUtils.getDaysLeft
+import org.briarproject.briar.desktop.expiration.ExpirationUtils.isExpired
+import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
+import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nP
+import org.briarproject.briar.desktop.utils.PreviewUtils.preview
+import kotlin.time.Duration.Companion.hours
+
+fun main() = preview {
+    Column {
+        ExpirationBanner {}
+    }
+}
+
+@Composable
+fun ExpirationBanner(onExpired: () -> Unit) {
+
+    var daysLeft by remember { mutableStateOf(0) }
+    LaunchedEffect(Unit) {
+        launch {
+            while (true) {
+                daysLeft = getDaysLeft()
+                if (isExpired()) {
+                    onExpired()
+                    break
+                }
+                delay(1.hours.inWholeMilliseconds)
+            }
+        }
+    }
+
+    ExpirationBanner(daysLeft)
+}
+
+@Composable
+fun ExpirationBanner(
+    daysLeft: Int,
+) = Surface(
+    color = MaterialTheme.colors.error,
+    modifier = Modifier.fillMaxWidth()
+) {
+    Row(
+        horizontalArrangement = Arrangement.spacedBy(8.dp),
+        verticalAlignment = Alignment.CenterVertically,
+        modifier = Modifier.padding(8.dp)
+    ) {
+        Icon(Icons.Filled.Warning, i18n("warning"))
+        Text(i18nP("expiration.banner", daysLeft))
+    }
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationUtils.kt b/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7a51a741e5a4f25c7bf859379cd40c803b0e0be0
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationUtils.kt
@@ -0,0 +1,12 @@
+package org.briarproject.briar.desktop.expiration
+
+import org.briarproject.briar.desktop.BuildData
+import java.time.Instant
+import java.time.temporal.ChronoUnit
+
+object ExpirationUtils {
+
+    fun getDaysLeft() = 90 - ChronoUnit.DAYS.between(Instant.ofEpochMilli(BuildData.GIT_TIME), Instant.now()).toInt()
+
+    fun isExpired() = getDaysLeft() <= 0
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
index dc7e70b570dd09e5ed64e6d27c3917e6638b9b50..871f581bf6a815b40e2591479fd34cb491b42785 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
@@ -56,9 +56,12 @@ import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.utils.PreviewUtils.preview
 
 fun main() = preview {
-    var error: ErrorSubViewModel.Error by remember { mutableStateOf(RegistrationSubViewModel.RegistrationError) }
+    var error: ErrorSubViewModel.Error by remember { mutableStateOf(ErrorSubViewModel.ExpirationError) }
 
     Row(horizontalArrangement = spacedBy(8.dp)) {
+        Button(onClick = { error = ErrorSubViewModel.ExpirationError }) {
+            Text("Expiration")
+        }
         Button(onClick = { error = RegistrationSubViewModel.RegistrationError }) {
             Text("Registration")
         }
@@ -100,6 +103,7 @@ fun ErrorScreen(
         Text(i18n("sorry"), style = MaterialTheme.typography.h5)
 
         val text = when (error) {
+            is ErrorSubViewModel.ExpirationError -> i18n("startup.failed.expired")
             is RegistrationSubViewModel.RegistrationError -> i18n("startup.failed.registration")
             is StartupViewModel.StartingError -> {
                 when (error.error) {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt
index 5f0c1dd228cd9a1dd44719fd2d8100e0490ec6c4..e4129ebdea14789586c081e07e4fec3d08f55f96 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt
@@ -24,4 +24,6 @@ class ErrorSubViewModel(
     val onBackButton: () -> Unit,
 ) : StartupViewModel.SubViewModel {
     sealed interface Error
+
+    object ExpirationError : Error
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt
index ecb09900eb5b9b3cae88b5dfa285b2562ba92890..8cd391466f051952da30243631ce36fcfc5cd03a 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt
@@ -30,6 +30,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS
 import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent
+import org.briarproject.briar.desktop.expiration.ExpirationUtils.isExpired
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.viewmodel.EventListenerDbViewModel
 import org.briarproject.briar.desktop.viewmodel.asState
@@ -60,8 +61,9 @@ constructor(
     private val _currentSubViewModel = mutableStateOf(decideSubViewModel())
     val currentSubViewModel = _currentSubViewModel.asState()
 
-    private fun decideSubViewModel() =
-        if (accountManager.accountExists()) makeLogin()
+    private fun decideSubViewModel(): SubViewModel =
+        if (isExpired()) makeError(ErrorSubViewModel.ExpirationError)
+        else if (accountManager.accountExists()) makeLogin()
         else makeRegistration()
 
     private fun makeLogin() = LoginSubViewModel(
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
index ef1141c379752b194049ccc500bbfbf272676c27..ffafaaf8c251f591343708a2121eb0242fef12df 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
@@ -18,12 +18,15 @@
 
 package org.briarproject.briar.desktop.ui
 
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.window.FrameWindowScope
 import androidx.compose.ui.window.Window
@@ -35,6 +38,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING
 import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent
 import org.briarproject.briar.desktop.DesktopFeatureFlags
+import org.briarproject.briar.desktop.expiration.ExpirationBanner
 import org.briarproject.briar.desktop.login.StartupScreen
 import org.briarproject.briar.desktop.settings.SettingsViewModel
 import org.briarproject.briar.desktop.theme.BriarTheme
@@ -114,9 +118,12 @@ constructor(
             ) {
                 val settingsViewModel: SettingsViewModel = viewModel()
                 BriarTheme(isDarkTheme = settingsViewModel.isDarkMode.value) {
-                    when (screenState) {
-                        STARTUP -> StartupScreen()
-                        MAIN -> MainScreen(settingsViewModel)
+                    Column(Modifier.fillMaxSize()) {
+                        ExpirationBanner(onExpired = { screenState = STARTUP })
+                        when (screenState) {
+                            STARTUP -> StartupScreen()
+                            MAIN -> MainScreen(settingsViewModel)
+                        }
                     }
                 }
             }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt
index d860f80b8f4cdfe80313b67ff66793ca3c6bb38e..67c1b749c3eeeb832aa123b7a3c5ee6ae4631883 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt
@@ -21,6 +21,7 @@ package org.briarproject.briar.desktop.ui
 import androidx.compose.ui.unit.dp
 
 object Constants {
+    val BANNER_HEIGHT = 24.dp
     val HEADER_SIZE = 56.dp
     val COLUMN_WIDTH = 275.dp
     val STARTUP_FIELDS_WIDTH = 400.dp
diff --git a/src/main/resources/strings/BriarDesktop.properties b/src/main/resources/strings/BriarDesktop.properties
index 7b65487cb91c483f896ed058c121e14e1322c33b..d5ae8d74ef2f875b0eeee2dfa55de3901bc7a1a7 100644
--- a/src/main/resources/strings/BriarDesktop.properties
+++ b/src/main/resources/strings/BriarDesktop.properties
@@ -121,6 +121,7 @@ next=Next
 open=Open
 sorry=Sorry
 error=Error
+warning=Warning
 unsupported_feature=Unfortunately, this feature is not yet supported by Briar Desktop.
 
 # Startup screen
@@ -142,6 +143,7 @@ startup.error.decryption.text=Briar cannot check your password. Please try reboo
 startup.password_forgotten.button=I have forgotten my password
 startup.password_forgotten.title=Lost Password
 startup.password_forgotten.text=Your Briar account is stored encrypted on your device, not in the cloud, so we can't reset your password. Would you like to delete your account and start again?\n\nCaution: Your identities, contacts and messages will be permanently lost.
+startup.failed.expired=This software has expired. Thank you for testing!\n\nTo continue using Briar, please download the latest release. You will be able to continue using your account.
 startup.failed.registration=Briar was unable to create your account.\n\nPlease upgrade to the latest version and try again.
 startup.failed.clock_error=Briar was unable to start because your device's clock is wrong.\n\nPlease set your device's clock to the right time and try again.
 startup.failed.db_error=Briar was unable to open the database containing your account, your contacts and your messages.\n\nPlease check if Briar is already running on this device. Otherwise, upgrade to the latest version of the app and try again, or set up a new account by choosing 'I have forgotten my password' at the password prompt.
@@ -152,6 +154,7 @@ startup.database.creating=Creating Account...
 startup.database.opening=Decrypting Database...
 startup.database.migrating=Upgrading Database...
 startup.database.compacting=Compacting Database...
+expiration.banner={0, plural, one {This is a test version of Briar that will expire in {0} day. Please update to a newer version in time.} other {This is a test version of Briar that will expire in {0} days. Please update to a newer version in time.}}
 
 # Settings
 settings.title=Settings