diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt
index 013a8c54..1ef8c942 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt
@@ -4,8 +4,8 @@ import com.simiacryptus.skyenet.core.actors.record.CodingActorInterceptor
 import com.simiacryptus.skyenet.core.actors.record.ImageActorInterceptor
 import com.simiacryptus.skyenet.core.actors.record.ParsedActorInterceptor
 import com.simiacryptus.skyenet.core.actors.record.SimpleActorInterceptor
-import com.simiacryptus.skyenet.core.platform.DataStorage
 import com.simiacryptus.skyenet.core.platform.Session
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import com.simiacryptus.skyenet.core.platform.User
 import com.simiacryptus.skyenet.core.util.FunctionWrapper
 import com.simiacryptus.skyenet.core.util.JsonFunctionRecorder
@@ -13,7 +13,7 @@ import java.io.File
 
 open class ActorSystem<T:Enum<*>>(
     private val actors: Map<T, BaseActor<*,*>>,
-    val dataStorage: DataStorage,
+    val dataStorage: StorageInterface,
     val user: User?,
     val session: Session
 ) {
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt
index d6f7a066..915a3d09 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt
@@ -47,7 +47,7 @@ open class ParsedActor<T>(
     }
     private val _obj: T by lazy { parser.apply(text) }
     override val text get() = _text
-    override fun getObj(clazz: Class<T>): T = _obj
+    override val obj get() = _obj
   }
 
   override fun answer(vararg messages: ApiModel.ChatMessage, input: List<String>, api: API): ParsedResponse<T> {
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt
index a9c51e3f..54b4ec29 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt
@@ -2,7 +2,6 @@ package com.simiacryptus.skyenet.core.actors
 
 abstract class ParsedResponse<T>(val clazz: Class<T>) {
     abstract val text: String
-    abstract fun getObj(clazz: Class<T>): T
-    val obj: T get() = getObj(clazz)
+    abstract val obj: T
     override fun toString() = text
 }
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt
index 7fdd863d..c81596c0 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt
@@ -1,6 +1,15 @@
 package com.simiacryptus.skyenet.core.platform
 
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.simiacryptus.jopenai.ApiModel
+import com.simiacryptus.jopenai.models.OpenAIModel
+import com.simiacryptus.skyenet.core.platform.file.*
+import org.junit.jupiter.api.Assertions.assertNull
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
 import java.io.File
+import java.util.*
+import java.util.concurrent.atomic.AtomicInteger
 
 object ApplicationServices {
     var isLocked: Boolean = false
@@ -8,27 +17,27 @@ object ApplicationServices {
             require(!isLocked) { "ApplicationServices is locked" }
             field = value
         }
-    var usageManager: UsageManager = UsageManager()
+    var usageManager: UsageInterface = UsageManager()
         set(value) {
             require(!isLocked) { "ApplicationServices is locked" }
             field = value
         }
-    var authorizationManager: AuthorizationManager = AuthorizationManager()
+    var authorizationManager: AuthorizationInterface = AuthorizationManager()
         set(value) {
             require(!isLocked) { "ApplicationServices is locked" }
             field = value
         }
-    var userSettingsManager: UserSettingsManager = UserSettingsManager()
+    var userSettingsManager: UserSettingsInterface = UserSettingsManager()
         set(value) {
             require(!isLocked) { "ApplicationServices is locked" }
             field = value
         }
-    var authenticationManager: AuthenticationManager = AuthenticationManager()
+    var authenticationManager: AuthenticationInterface = AuthenticationManager()
         set(value) {
             require(!isLocked) { "ApplicationServices is locked" }
             field = value
         }
-    var dataStorageFactory: (File) -> DataStorage = { DataStorage(it) }
+    var dataStorageFactory: (File) -> StorageInterface = { DataStorage(it) }
         set(value) {
             require(!isLocked) { "ApplicationServices is locked" }
             field = value
@@ -39,4 +48,239 @@ object ApplicationServices {
             field = value
         }
 
-}
\ No newline at end of file
+}
+
+interface AuthenticationInterface {
+    fun getUser(accessToken: String?): User?
+
+    fun containsUser(value: String): Boolean
+
+    fun putUser(accessToken: String, user: User): User
+    fun logout(accessToken: String, user: User)
+
+    companion object {
+        const val AUTH_COOKIE = "sessionId"
+    }
+}
+
+interface AuthorizationInterface {
+    enum class OperationType {
+        Read,
+        Write,
+        Share,
+        Execute,
+        Delete,
+        Admin,
+        GlobalKey,
+    }
+
+    fun isAuthorized(
+        applicationClass: Class<*>?,
+        user: User?,
+        operationType: OperationType,
+    ): Boolean
+}
+
+data class User(
+    @get:JsonProperty("email") internal val email: String,
+    @get:JsonProperty("name") internal val name: String? = null,
+    @get:JsonProperty("id") internal val id: String? = null,
+    @get:JsonProperty("picture") internal val picture: String? = null,
+) {
+    override fun toString() = email
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as User
+
+        return email == other.email
+    }
+
+    override fun hashCode(): Int {
+        return email.hashCode()
+    }
+
+}
+
+data class Session(
+    internal val sessionId: String
+) {
+    init {
+        StorageInterface.validateSessionId(this)
+    }
+
+    override fun toString() = sessionId
+}
+
+interface StorageInterface {
+    fun <T> getJson(
+        user: User?,
+        session: Session,
+        filename: String,
+        clazz: Class<T>
+    ): T?
+
+    fun getMessages(
+        user: User?,
+        session: Session
+    ): LinkedHashMap<String, String>
+
+    fun getSessionDir(
+        user: User?,
+        session: Session
+    ): File
+
+    fun getSessionName(
+        user: User?,
+        session: Session
+    ): String
+
+    fun getSessionTime(
+        user: User?,
+        session: Session
+    ): Date?
+
+    fun listSessions(
+        user: User?
+    ): List<Session>
+
+    fun <T : Any> setJson(
+        user: User?,
+        session: Session,
+        filename: String,
+        settings: T
+    ): T
+
+    fun updateMessage(
+        user: User?,
+        session: Session,
+        messageId: String,
+        value: String
+    )
+
+    fun listSessions(dir: File): List<String>
+    fun userRoot(user: User?): File
+    fun deleteSession(user: User?, session: Session)
+
+    companion object {
+
+        fun validateSessionId(
+            session: Session
+        ) {
+            if (!session.sessionId.matches("""([GU]-)?\d{8}-\w{8}""".toRegex())) {
+                throw IllegalArgumentException("Invalid session ID: $session")
+            }
+        }
+
+        fun newGlobalID(): Session {
+            val uuid = UUID.randomUUID().toString().split("-").first()
+            val yyyyMMdd = java.time.LocalDate.now().toString().replace("-", "")
+            //log.debug("New ID: $yyyyMMdd-$uuid")
+            return Session("G-$yyyyMMdd-$uuid")
+        }
+
+        fun newUserID(): Session {
+            val uuid = UUID.randomUUID().toString().split("-").first()
+            val yyyyMMdd = java.time.LocalDate.now().toString().replace("-", "")
+            //log.debug("New ID: $yyyyMMdd-$uuid")
+            return Session("U-$yyyyMMdd-$uuid")
+        }
+
+    }
+}
+
+
+class StorageInterfaceTest(val storage: StorageInterface) {
+
+
+    @Test
+    fun testGetJson() {
+        // Arrange
+        val user = User(email = "test@example.com")
+        val session = Session("G-20230101-12345678")
+        val filename = "test.json"
+
+        // Act
+        val result = storage.getJson(user, session, filename, Any::class.java)
+
+        // Assert
+        assertNull(result, "Expected null result for non-existing JSON file")
+    }
+
+    @Test
+    fun testGetMessages() {
+        // Arrange
+        val user = User(email = "test@example.com")
+        val session = Session("G-20230101-12345678")
+
+        // Act
+        val messages = storage.getMessages(user, session)
+
+        // Assert
+        assertTrue(messages is LinkedHashMap<*, *>, "Expected LinkedHashMap type for messages")
+    }
+
+    @Test
+    fun testGetSessionDir() {
+        // Arrange
+        val user = User(email = "test@example.com")
+        val session = Session("G-20230101-12345678")
+
+        // Act
+        val sessionDir = storage.getSessionDir(user, session)
+
+        // Assert
+        assertTrue(sessionDir is File, "Expected File type for session directory")
+    }
+
+    // Continue writing tests for each method in StorageInterface...
+    // ...
+}
+
+interface UsageInterface {
+    fun incrementUsage(session: Session, user: User?, model: OpenAIModel, tokens: ApiModel.Usage)
+
+    fun getUserUsageSummary(user: User): Map<OpenAIModel, ApiModel.Usage>
+
+    fun getSessionUsageSummary(session: Session): Map<OpenAIModel, ApiModel.Usage>
+
+    data class UsageKey(
+        val session: Session,
+        val user: User?,
+        val model: OpenAIModel,
+    )
+
+    class UsageValues(
+        val inputTokens: AtomicInteger = AtomicInteger(),
+        val outputTokens: AtomicInteger = AtomicInteger(),
+    ) {
+        fun addAndGet(tokens: ApiModel.Usage) {
+            inputTokens.addAndGet(tokens.prompt_tokens)
+            outputTokens.addAndGet(tokens.completion_tokens)
+        }
+
+        fun toUsage() = ApiModel.Usage(
+            prompt_tokens = inputTokens.get(),
+            completion_tokens = outputTokens.get()
+        )
+    }
+
+    class UsageCounters(
+        val tokensPerModel: HashMap<UsageKey, UsageValues> = HashMap(),
+    )
+}
+
+
+interface UserSettingsInterface {
+    data class UserSettings(
+        val apiKey: String = "",
+    )
+
+    fun getUserSettings(user: User): UserSettings
+
+    fun updateUserSettings(user: User, settings: UserSettings)
+}
+
+
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManager.kt
deleted file mode 100644
index 409e432b..00000000
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManager.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.simiacryptus.skyenet.core.platform
-
-open class AuthenticationManager {
-
-    private val users = HashMap<String, User>()
-
-    open fun getUser(accessToken: String?) = if (null == accessToken) null else users[accessToken]
-
-    open fun containsUser(value: String): Boolean = users.containsKey(value)
-
-    open fun putUser(accessToken: String, user: User): User {
-        users[accessToken] = user
-        return user
-    }
-
-    fun logout(accessToken: String, user: User) {
-        require(users[accessToken] == user) { "Invalid user" }
-        users.remove(accessToken)
-    }
-
-    companion object {
-        const val AUTH_COOKIE = "sessionId"
-    }
-}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt
index c227163d..9229376c 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt
@@ -1,20 +1,21 @@
 package com.simiacryptus.skyenet.core.platform
 
-import com.google.common.util.concurrent.ListeningExecutorService
 import com.google.common.util.concurrent.ListeningScheduledExecutorService
-import com.google.common.util.concurrent.MoreExecutors
 import com.google.common.util.concurrent.ThreadFactoryBuilder
 import com.simiacryptus.jopenai.ApiModel
 import com.simiacryptus.jopenai.ClientUtil
 import com.simiacryptus.jopenai.HttpClientManager
 import com.simiacryptus.jopenai.OpenAIClient
-
 import com.simiacryptus.jopenai.models.OpenAIModel
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType
 import org.apache.hc.client5.http.impl.classic.CloseableHttpClient
 import org.slf4j.LoggerFactory
 import org.slf4j.event.Level
 import java.io.File
-import java.util.concurrent.*
+import java.util.concurrent.SynchronousQueue
+import java.util.concurrent.ThreadFactory
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
 
 open class ClientManager {
 
@@ -26,17 +27,25 @@ open class ClientManager {
   fun getClient(
     session: Session,
     user: User?,
-    dataStorage: DataStorage?,
+    dataStorage: StorageInterface?,
   ): OpenAIClient {
     val key = SessionKey(session, user)
     return if (null == dataStorage) clientCache[key] ?: throw IllegalStateException("No data storage")
     else clientCache.getOrPut(key) { createClient(session, user, dataStorage) }
   }
 
+  protected open fun createPool(session: Session, user: User?, dataStorage: StorageInterface?) =
+    ThreadPoolExecutor(
+      0, Integer.MAX_VALUE,
+      500, TimeUnit.MILLISECONDS,
+      SynchronousQueue(),
+      RecordingThreadFactory(session, user)
+    )
+
   fun getPool(
     session: Session,
     user: User?,
-    dataStorage: DataStorage?,
+    dataStorage: StorageInterface?,
   ): ThreadPoolExecutor {
     val key = SessionKey(session, user)
     return poolCache.getOrPut(key) {
@@ -44,17 +53,9 @@ open class ClientManager {
     }
   }
 
-  protected open fun createPool(session: Session, user: User?, dataStorage: DataStorage?) =
-    ThreadPoolExecutor(
-      0, Integer.MAX_VALUE,
-      500, TimeUnit.MILLISECONDS,
-      SynchronousQueue(),
-      RecordingThreadFactory(session, user)
-    )
-
   inner class RecordingThreadFactory(
-    private val session: Session,
-    private val user: User?
+    session: Session,
+    user: User?
   ) : ThreadFactory {
     private val inner = ThreadFactoryBuilder().setNameFormat("Session $session; User $user; #%d").build()
     val threads = mutableSetOf<Thread>()
@@ -69,14 +70,14 @@ open class ClientManager {
   protected open fun createClient(
     session: Session,
     user: User?,
-    dataStorage: DataStorage?,
+    dataStorage: StorageInterface?,
   ): OpenAIClient {
     if (user != null) {
       val userSettings = ApplicationServices.userSettingsManager.getUserSettings(user)
       val logfile = dataStorage?.getSessionDir(user, session)?.resolve(".sys/openai.log")
       logfile?.parentFile?.mkdirs()
       val userApi =
-        if (userSettings.apiKey.isNullOrBlank()) null else MonitoredClient(
+        if (userSettings.apiKey.isBlank()) null else MonitoredClient(
           key = userSettings.apiKey,
           logfile = logfile,
           session = session,
@@ -86,12 +87,12 @@ open class ClientManager {
       if (userApi != null) return userApi
     }
     val canUseGlobalKey = ApplicationServices.authorizationManager.isAuthorized(
-      null, user, AuthorizationManager.OperationType.GlobalKey
+      null, user, OperationType.GlobalKey
     )
     if (!canUseGlobalKey) throw RuntimeException("No API key")
     val logfile = dataStorage?.getSessionDir(user, session)?.resolve(".sys/openai.log")
     logfile?.parentFile?.mkdirs()
-    return (if (ClientUtil.keyTxt.isNullOrBlank()) null else MonitoredClient(
+    return (if (ClientUtil.keyTxt.isBlank()) null else MonitoredClient(
       key = ClientUtil.keyTxt,
       logfile = logfile,
       session = session,
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/Session.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/Session.kt
deleted file mode 100644
index ec2e1bce..00000000
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/Session.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.simiacryptus.skyenet.core.platform
-
-import com.simiacryptus.skyenet.core.platform.DataStorage.Companion.validateSessionId
-
-data class Session(
-    internal val sessionId: String
-) {
-    init {
-        validateSessionId(this)
-    }
-
-    override fun toString() = sessionId
-}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/User.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/User.kt
deleted file mode 100644
index a4dfeb9a..00000000
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/User.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.simiacryptus.skyenet.core.platform
-
-import com.fasterxml.jackson.annotation.JsonIgnore
-import com.fasterxml.jackson.annotation.JsonProperty
-
-data class User(
-    @get:JsonProperty("email") internal val email: String,
-    @get:JsonProperty("name") internal val name: String? = null,
-    @get:JsonProperty("id") internal val id: String? = null,
-    @get:JsonProperty("picture") internal val picture: String? = null,
-) {
-    override fun toString() = email
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (javaClass != other?.javaClass) return false
-
-        other as User
-
-        return email == other.email
-    }
-
-    override fun hashCode(): Int {
-        return email.hashCode()
-    }
-
-}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthenticationManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthenticationManager.kt
new file mode 100644
index 00000000..8511b6e5
--- /dev/null
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthenticationManager.kt
@@ -0,0 +1,24 @@
+package com.simiacryptus.skyenet.core.platform.file
+
+import com.simiacryptus.skyenet.core.platform.AuthenticationInterface
+import com.simiacryptus.skyenet.core.platform.User
+
+open class AuthenticationManager : AuthenticationInterface {
+
+    private val users = HashMap<String, User>()
+
+    override fun getUser(accessToken: String?) = if (null == accessToken) null else users[accessToken]
+
+    override fun containsUser(value: String): Boolean = users.containsKey(value)
+
+    override fun putUser(accessToken: String, user: User): User {
+        users[accessToken] = user
+        return user
+    }
+
+    override fun logout(accessToken: String, user: User) {
+        require(users[accessToken] == user) { "Invalid user" }
+        users.remove(accessToken)
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthorizationManager.kt
similarity index 76%
rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManager.kt
rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthorizationManager.kt
index 9eca3385..793ff876 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManager.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthorizationManager.kt
@@ -1,23 +1,15 @@
-package com.simiacryptus.skyenet.core.platform
+package com.simiacryptus.skyenet.core.platform.file
 
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface
+import com.simiacryptus.skyenet.core.platform.User
 import java.util.*
 
-open class AuthorizationManager {
+open class AuthorizationManager : AuthorizationInterface {
 
-    enum class OperationType {
-        Read,
-        Write,
-        Share,
-        Execute,
-        Delete,
-        Admin,
-        GlobalKey,
-    }
-
-    open fun isAuthorized(
+    override fun isAuthorized(
         applicationClass: Class<*>?,
         user: User?,
-        operationType: OperationType,
+        operationType: AuthorizationInterface.OperationType,
     ) = try {
         if (isUserAuthorized("/permissions/${operationType.name.lowercase(Locale.getDefault())}.txt", user)) {
             log.debug("User {} authorized for {} globally", user, operationType)
@@ -41,7 +33,7 @@ open class AuthorizationManager {
         false
     }
 
-    open fun isUserAuthorized(permissionPath: String, user: User?) =
+    fun isUserAuthorized(permissionPath: String, user: User?) =
         javaClass.getResourceAsStream(permissionPath)?.use { stream ->
             val lines = stream.bufferedReader().readLines()
             lines.any { line ->
@@ -56,6 +48,8 @@ open class AuthorizationManager {
         else -> false
     }
 
-    private val log = org.slf4j.LoggerFactory.getLogger(AuthorizationManager::class.java)
+    companion object {
+        private val log = org.slf4j.LoggerFactory.getLogger(AuthorizationManager::class.java)
+    }
 
 }
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/DataStorage.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt
similarity index 78%
rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/DataStorage.kt
rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt
index 7d7ad62b..93130f3e 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/DataStorage.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt
@@ -1,15 +1,19 @@
-package com.simiacryptus.skyenet.core.platform
+package com.simiacryptus.skyenet.core.platform.file
 
 import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.skyenet.core.platform.Session
+import com.simiacryptus.skyenet.core.platform.StorageInterface
+import com.simiacryptus.skyenet.core.platform.StorageInterface.Companion.validateSessionId
+import com.simiacryptus.skyenet.core.platform.User
 import java.io.File
 import java.util.*
 
 
 open class DataStorage(
     private val dataDir: File
-) {
+) : StorageInterface {
 
-    open fun <T> getJson(
+    override fun <T> getJson(
         user: User?,
         session: Session,
         filename: String,
@@ -22,7 +26,7 @@ open class DataStorage(
         }
     }
 
-    open fun getMessages(
+    override fun getMessages(
         user: User?,
         session: Session
     ): LinkedHashMap<String, String> {
@@ -38,7 +42,7 @@ open class DataStorage(
         return messages
     }
 
-    open fun getSessionDir(
+    override fun getSessionDir(
         user: User?,
         session: Session
     ): File {
@@ -72,12 +76,12 @@ open class DataStorage(
         }
     }
 
-    open fun getSessionName(
+    override fun getSessionName(
         user: User?,
         session: Session
     ): String {
         validateSessionId(session)
-        val userMessage = messages(user, session).entries.minByOrNull { it.key.lastModified() }?.value
+        val userMessage = messageFiles(user, session).entries.minByOrNull { it.key.lastModified() }?.value
         return if (null != userMessage) {
             //log.debug("Session {}: {}", session, userMessage)
             userMessage
@@ -87,12 +91,12 @@ open class DataStorage(
         }
     }
 
-    open fun getSessionTime(
+    override fun getSessionTime(
         user: User?,
         session: Session
     ): Date? {
         validateSessionId(session)
-        val file = messages(user, session).entries.minByOrNull { it.key.lastModified() }?.key
+        val file = messageFiles(user, session).entries.minByOrNull { it.key.lastModified() }?.key
         return if (null != file) {
             Date(file.lastModified())
         } else {
@@ -101,7 +105,7 @@ open class DataStorage(
         }
     }
 
-    private fun messages(
+    fun messageFiles(
         user: User?,
         session: Session
     ) = File(this.getSessionDir(user, session), MESSAGE_DIR).listFiles()
@@ -123,7 +127,7 @@ open class DataStorage(
             }
         }?.filter { it.second.isNotEmpty() }?.toList()?.toMap() ?: mapOf()
 
-    open fun listSessions(
+    override fun listSessions(
         user: User?
     ): List<Session> {
         val globalSessions = listSessions(dataDir)
@@ -131,7 +135,7 @@ open class DataStorage(
         return globalSessions.map { Session("G-$it") } + userSessions.map { Session("U-$it") }
     }
 
-    open fun <T : Any> setJson(
+    override fun <T : Any> setJson(
         user: User?,
         session: Session,
         filename: String,
@@ -144,7 +148,7 @@ open class DataStorage(
         return settings
     }
 
-    open fun updateMessage(
+    override fun updateMessage(
         user: User?,
         session: Session,
         messageId: String,
@@ -157,7 +161,7 @@ open class DataStorage(
         JsonUtil.objectMapper().writeValue(file, value)
     }
 
-    private fun listSessions(dir: File): List<String> {
+    override fun listSessions(dir: File): List<String> {
         val files = dir.listFiles()?.flatMap { it.listFiles()?.toList() ?: listOf() }?.filter { sessionDir ->
             val operationDir = File(sessionDir, MESSAGE_DIR)
             if (!operationDir.exists()) false else {
@@ -169,12 +173,12 @@ open class DataStorage(
         return files.map { it.parentFile.name + "-" + it.name }
     }
 
-    private fun userRoot(user: User?) = File(
+    override fun userRoot(user: User?) = File(
         File(dataDir, "users"),
         user?.email ?: throw IllegalArgumentException("User required for private session")
     )
 
-    fun deleteSession(user: User?, session: Session) {
+    override fun deleteSession(user: User?, session: Session) {
         validateSessionId(session)
         val sessionDir = getSessionDir(user, session)
         sessionDir.deleteRecursively()
@@ -182,29 +186,7 @@ open class DataStorage(
 
     companion object {
 
-        private val log = org.slf4j.LoggerFactory.getLogger(DataStorage::class.java)
-
-        fun validateSessionId(
-            session: Session
-        ) {
-            if (!session.sessionId.matches("""([GU]-)?\d{8}-\w{8}""".toRegex())) {
-                throw IllegalArgumentException("Invalid session ID: $session")
-            }
-        }
-
-        fun newGlobalID(): Session {
-            val uuid = UUID.randomUUID().toString().split("-").first()
-            val yyyyMMdd = java.time.LocalDate.now().toString().replace("-", "")
-            //log.debug("New ID: $yyyyMMdd-$uuid")
-            return Session("G-$yyyyMMdd-$uuid")
-        }
-
-        fun newUserID(): Session {
-            val uuid = UUID.randomUUID().toString().split("-").first()
-            val yyyyMMdd = java.time.LocalDate.now().toString().replace("-", "")
-            //log.debug("New ID: $yyyyMMdd-$uuid")
-            return Session("U-$yyyyMMdd-$uuid")
-        }
+        private val log = org.slf4j.LoggerFactory.getLogger(StorageInterface::class.java)
 
         private val MESSAGE_DIR = ".sys" + File.separator + "messages"
 
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UsageManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt
similarity index 83%
rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UsageManager.kt
rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt
index a51147f2..d2a53f0c 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UsageManager.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt
@@ -1,15 +1,18 @@
-package com.simiacryptus.skyenet.core.platform
+package com.simiacryptus.skyenet.core.platform.file
 
 import com.simiacryptus.jopenai.models.*
 import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.skyenet.core.platform.Session
+import com.simiacryptus.skyenet.core.platform.UsageInterface
+import com.simiacryptus.skyenet.core.platform.UsageInterface.*
+import com.simiacryptus.skyenet.core.platform.User
 import java.io.File
 import java.io.FileWriter
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit
-import java.util.concurrent.atomic.AtomicInteger
 
-open class UsageManager {
+open class UsageManager : UsageInterface {
 
     private val scheduler = Executors.newSingleThreadScheduledExecutor()
     private val txLogFile = File(".skyenet/usage/log.csv")
@@ -26,7 +29,7 @@ open class UsageManager {
     }
 
     @Suppress("MemberVisibilityCanBePrivate")
-    open fun loadFromLog(file: File) {
+    private fun loadFromLog(file: File) {
         if (file.exists()) {
             try {
                 file.readLines().forEach { line ->
@@ -63,7 +66,7 @@ open class UsageManager {
     }
 
     @Suppress("MemberVisibilityCanBePrivate")
-    protected open fun writeCompactLog(file: File) {
+    private fun writeCompactLog(file: File) {
         FileWriter(file).use { writer ->
             usagePerSession.forEach { (sessionId, usage) ->
                 val user = usersBySession[sessionId]?.firstOrNull()
@@ -107,7 +110,7 @@ open class UsageManager {
         File(".skyenet/usage/counters.json").writeText(JsonUtil.toJson(usagePerSession))
     }
 
-    open fun incrementUsage(session: Session, user: User?, model: OpenAIModel, tokens: com.simiacryptus.jopenai.ApiModel.Usage) {
+    override fun incrementUsage(session: Session, user: User?, model: OpenAIModel, tokens: com.simiacryptus.jopenai.ApiModel.Usage) {
         @Suppress("NAME_SHADOWING") val user = if (null == user) null else User(email = user.email) // Hack
         usagePerSession.computeIfAbsent(session) { UsageCounters() }
             .tokensPerModel.computeIfAbsent(UsageKey(session, user, model)) { UsageValues() }
@@ -129,7 +132,7 @@ open class UsageManager {
         }
     }
 
-    open fun getUserUsageSummary(user: User): Map<OpenAIModel, com.simiacryptus.jopenai.ApiModel.Usage> {
+    override fun getUserUsageSummary(user: User): Map<OpenAIModel, com.simiacryptus.jopenai.ApiModel.Usage> {
         @Suppress("NAME_SHADOWING") val user = if(null == user) null else User(email= user.email) // Hack
         return sessionsByUser[user]?.flatMap { sessionId ->
             val usage = usagePerSession[sessionId]
@@ -146,7 +149,7 @@ open class UsageManager {
         } ?: emptyMap()
     }
 
-    open fun getSessionUsageSummary(session: Session): Map<OpenAIModel, com.simiacryptus.jopenai.ApiModel.Usage> =
+    override fun getSessionUsageSummary(session: Session): Map<OpenAIModel, com.simiacryptus.jopenai.ApiModel.Usage> =
         usagePerSession[session]?.tokensPerModel?.entries?.map { (model, counter) ->
             model.model to counter.toUsage()
         }?.groupBy { it.first }?.mapValues { it.value.map { it.second }.reduce { a, b ->
@@ -156,31 +159,6 @@ open class UsageManager {
             )
         } } ?: emptyMap()
 
-    data class UsageKey(
-        val session: Session,
-        val user: User?,
-        val model: OpenAIModel,
-    )
-
-    class UsageValues(
-        val inputTokens: AtomicInteger = AtomicInteger(),
-        val outputTokens: AtomicInteger = AtomicInteger(),
-    ) {
-        fun addAndGet(tokens: com.simiacryptus.jopenai.ApiModel.Usage) {
-            inputTokens.addAndGet(tokens.prompt_tokens)
-            outputTokens.addAndGet(tokens.completion_tokens)
-        }
-
-        fun toUsage() = com.simiacryptus.jopenai.ApiModel.Usage(
-            prompt_tokens = inputTokens.get(),
-            completion_tokens = outputTokens.get()
-        )
-    }
-
-    data class UsageCounters(
-        val tokensPerModel: HashMap<UsageKey, UsageValues> = HashMap(),
-    )
-
     companion object {
         private val log = org.slf4j.LoggerFactory.getLogger(UsageManager::class.java)
     }
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UserSettingsManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt
similarity index 68%
rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UserSettingsManager.kt
rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt
index 47423e07..cbc09cf1 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UserSettingsManager.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt
@@ -1,17 +1,17 @@
-package com.simiacryptus.skyenet.core.platform
+package com.simiacryptus.skyenet.core.platform.file
 
 import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.skyenet.core.platform.User
+import com.simiacryptus.skyenet.core.platform.UserSettingsInterface
+import com.simiacryptus.skyenet.core.platform.UserSettingsInterface.UserSettings
 import java.io.File
 
-open class UserSettingsManager {
-    data class UserSettings(
-        val apiKey: String = "",
-    )
+open class UserSettingsManager : UserSettingsInterface {
 
     private val userSettings = HashMap<User, UserSettings>()
     private val userConfigDirectory = File(".skyenet/users")
 
-    open fun getUserSettings(user: User): UserSettings {
+    override fun getUserSettings(user: User): UserSettings {
         return userSettings.getOrPut(user) {
             val file = File(userConfigDirectory, "$user.json")
             if (file.exists()) {
@@ -24,7 +24,7 @@ open class UserSettingsManager {
         }
     }
 
-    open fun updateUserSettings(user: User, settings: UserSettings) {
+    override fun updateUserSettings(user: User, settings: UserSettings) {
         userSettings[user] = settings
         val file = File(userConfigDirectory, "$user.json")
         file.parentFile.mkdirs()
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthenticationInterfaceTest.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthenticationInterfaceTest.kt
new file mode 100644
index 00000000..70c93556
--- /dev/null
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthenticationInterfaceTest.kt
@@ -0,0 +1,57 @@
+
+import com.simiacryptus.skyenet.core.platform.AuthenticationInterface
+import com.simiacryptus.skyenet.core.platform.User
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.Test
+import java.util.*
+
+open class AuthenticationInterfaceTest(
+  val authInterface: AuthenticationInterface
+) {
+
+  private val validAccessToken = UUID.randomUUID().toString()
+  private val newUser = User(email = "newuser@example.com", name = "Jane Smith", id = "2", picture = "http://example.com/newpicture.jpg")
+
+  @Test
+  fun `getUser should return null when no user is associated with access token`() {
+    val user = authInterface.getUser(validAccessToken)
+    assertNull(user)
+  }
+
+  @Test
+  fun `putUser should add a new user and return the user`() {
+    val returnedUser = authInterface.putUser(validAccessToken, newUser)
+    assertEquals(newUser, returnedUser)
+  }
+
+  @Test
+  fun `getUser should return User after putUser is called`() {
+    authInterface.putUser(validAccessToken, newUser)
+    val user = authInterface.getUser(validAccessToken)
+    assertNotNull(user)
+    assertEquals(newUser, user)
+  }
+
+  @Test
+  fun `containsUser should return false for non-existing user`() {
+    val exists = authInterface.containsUser(newUser.email)
+    assertFalse(exists)
+  }
+
+  @Test
+  fun `containsUser should return true after user is added`() {
+    authInterface.putUser(validAccessToken, newUser)
+    val exists = authInterface.containsUser(newUser.email)
+    assertTrue(exists)
+  }
+
+  @Test
+  fun `logout should remove the user associated with the access token`() {
+    authInterface.putUser(validAccessToken, newUser)
+    assertNotNull(authInterface.getUser(validAccessToken))
+
+    authInterface.logout(validAccessToken, newUser)
+    assertNull(authInterface.getUser(validAccessToken))
+  }
+
+}
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthorizationInterfaceTest.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthorizationInterfaceTest.kt
new file mode 100644
index 00000000..5723462d
--- /dev/null
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthorizationInterfaceTest.kt
@@ -0,0 +1,18 @@
+
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface
+import com.simiacryptus.skyenet.core.platform.User
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+
+open class AuthorizationInterfaceTest(
+  val authInterface: AuthorizationInterface
+) {
+
+  open val user = User(email = "newuser@example.com", name = "Jane Smith", id = "2", picture = "http://example.com/newpicture.jpg")
+
+  @Test
+  fun `newUser has admin`() {
+    assertTrue(authInterface.isAuthorized(this.javaClass, user, AuthorizationInterface.OperationType.Admin))
+  }
+
+}
diff --git a/core/src/test/java/com/simiacryptus/skyenet/core/DataStorageTest.kt b/core/src/test/java/com/simiacryptus/skyenet/core/DataStorageTest.kt
index b9e2d228..0bcaa38f 100644
--- a/core/src/test/java/com/simiacryptus/skyenet/core/DataStorageTest.kt
+++ b/core/src/test/java/com/simiacryptus/skyenet/core/DataStorageTest.kt
@@ -1,6 +1,7 @@
 package com.simiacryptus.skyenet.core
 
-import com.simiacryptus.skyenet.core.platform.DataStorage
+import com.simiacryptus.skyenet.core.platform.file.DataStorage
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import org.junit.jupiter.api.AfterEach
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Assertions.assertTrue
@@ -11,7 +12,7 @@ import java.nio.file.Files
 
 class DataStorageTest {
     private lateinit var tempDir: File
-    private lateinit var storage: DataStorage
+    private lateinit var storage: StorageInterface
 
     @BeforeEach
     fun setUp() {
@@ -26,7 +27,7 @@ class DataStorageTest {
 
     @Test
     fun testUpdateAndLoadMessage() {
-        val sessionId = DataStorage.newGlobalID()
+        val sessionId = StorageInterface.newGlobalID()
         val messageId = "message1"
         val message = "This is a test message."
 
diff --git a/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManagerTest.kt b/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManagerTest.kt
new file mode 100644
index 00000000..02882b57
--- /dev/null
+++ b/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManagerTest.kt
@@ -0,0 +1,6 @@
+package com.simiacryptus.skyenet.core.platform
+
+import AuthenticationInterfaceTest
+import com.simiacryptus.skyenet.core.platform.file.AuthenticationManager
+
+class AuthenticationManagerTest : AuthenticationInterfaceTest(AuthenticationManager()) {}
\ No newline at end of file
diff --git a/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManagerTest.kt b/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManagerTest.kt
new file mode 100644
index 00000000..5cdff9db
--- /dev/null
+++ b/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManagerTest.kt
@@ -0,0 +1,6 @@
+package com.simiacryptus.skyenet.core.platform
+
+import AuthorizationInterfaceTest
+import com.simiacryptus.skyenet.core.platform.file.AuthorizationManager
+
+class AuthorizationManagerTest : AuthorizationInterfaceTest(AuthorizationManager()) {}
\ No newline at end of file
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt
index 87e81999..a27310bb 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt
@@ -6,6 +6,7 @@ import com.simiacryptus.skyenet.core.actors.ActorSystem
 import com.simiacryptus.skyenet.core.actors.CodingActor
 import com.simiacryptus.skyenet.core.actors.CodingActor.CodeResult
 import com.simiacryptus.skyenet.core.platform.*
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType
 import com.simiacryptus.skyenet.webui.application.ApplicationInterface
 import com.simiacryptus.skyenet.webui.session.SessionTask
 import com.simiacryptus.skyenet.webui.util.MarkdownUtil
@@ -15,7 +16,7 @@ import kotlin.reflect.KClass
 
 class CodingAgent<T:Interpreter>(
         val api: API,
-        dataStorage: DataStorage,
+        dataStorage: StorageInterface,
         session: Session,
         user: User?,
         val ui: ApplicationInterface,
@@ -67,7 +68,7 @@ class CodingAgent<T:Interpreter>(
             val canPlay = ApplicationServices.authorizationManager.isAuthorized(
                 this::class.java,
                 user,
-                AuthorizationManager.OperationType.Execute
+              OperationType.Execute
             )
             val playLink = task.add(if (!canPlay) "" else {
                 ui.hrefLink("▶", "href-link play-button") {
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationServer.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationServer.kt
index cab6f281..71d6ff98 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationServer.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationServer.kt
@@ -1,14 +1,11 @@
 package com.simiacryptus.skyenet.webui.application
 
 import com.simiacryptus.jopenai.API
+import com.simiacryptus.skyenet.core.platform.*
 import com.simiacryptus.skyenet.core.platform.ApplicationServices.authenticationManager
 import com.simiacryptus.skyenet.core.platform.ApplicationServices.authorizationManager
 import com.simiacryptus.skyenet.core.platform.ApplicationServices.dataStorageFactory
-import com.simiacryptus.skyenet.core.platform.AuthenticationManager.Companion.AUTH_COOKIE
-import com.simiacryptus.skyenet.core.platform.AuthorizationManager
-import com.simiacryptus.skyenet.core.platform.DataStorage
-import com.simiacryptus.skyenet.core.platform.Session
-import com.simiacryptus.skyenet.core.platform.User
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType
 import com.simiacryptus.skyenet.webui.chat.ChatServer
 import com.simiacryptus.skyenet.webui.servlet.*
 import com.simiacryptus.skyenet.webui.session.SocketManager
@@ -28,7 +25,7 @@ abstract class ApplicationServer(
 
     open val description: String = ""
 
-    final override val dataStorage: DataStorage = dataStorageFactory(File(File(".skyenet"), applicationName))
+    final override val dataStorage: StorageInterface = dataStorageFactory(File(File(".skyenet"), applicationName))
     protected open val appInfo = ServletHolder("appInfo", AppInfoServlet(applicationName))
     protected open val userInfo = ServletHolder("userInfo", UserInfoServlet())
     protected open val usageServlet = ServletHolder("usage", UsageServlet())
@@ -98,7 +95,7 @@ abstract class ApplicationServer(
                 val canRead = authorizationManager.isAuthorized(
                     applicationClass = this@ApplicationServer.javaClass,
                     user = user,
-                    operationType = AuthorizationManager.OperationType.Read
+                    operationType = OperationType.Read
                 )
                 if (canRead) {
                     chain?.doFilter(request, response)
@@ -137,7 +134,8 @@ abstract class ApplicationServer(
                 filename.endsWith(".css") -> "text/css"
                 else -> "text/plain"
             }
-        fun HttpServletRequest.getCookie(name: String = AUTH_COOKIE) = cookies?.find { it.name == name }?.value
+        fun HttpServletRequest.getCookie(name: String = AuthenticationInterface.AUTH_COOKIE) =
+            cookies?.find { it.name == name }?.value
 
     }
 
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationSocketManager.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationSocketManager.kt
index 18f672d9..551b4936 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationSocketManager.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationSocketManager.kt
@@ -2,8 +2,8 @@ package com.simiacryptus.skyenet.webui.application
 
 import com.simiacryptus.jopenai.API
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.DataStorage
 import com.simiacryptus.skyenet.core.platform.Session
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import com.simiacryptus.skyenet.core.platform.User
 import com.simiacryptus.skyenet.webui.chat.ChatSocket
 import com.simiacryptus.skyenet.webui.session.SessionTask
@@ -13,7 +13,7 @@ import java.util.function.Consumer
 abstract class ApplicationSocketManager(
     session: Session,
     user: User?,
-    dataStorage: DataStorage?,
+    dataStorage: StorageInterface?,
     applicationClass: Class<*>,
 ) : SocketManagerBase(
     session = session,
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/chat/ChatServer.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/chat/ChatServer.kt
index 0e2dffbe..ee037a46 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/chat/ChatServer.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/chat/ChatServer.kt
@@ -1,10 +1,7 @@
 package com.simiacryptus.skyenet.webui.chat
 
+import com.simiacryptus.skyenet.core.platform.*
 import com.simiacryptus.skyenet.core.platform.ApplicationServices.authenticationManager
-import com.simiacryptus.skyenet.core.platform.AuthenticationManager.Companion.AUTH_COOKIE
-import com.simiacryptus.skyenet.core.platform.DataStorage
-import com.simiacryptus.skyenet.core.platform.Session
-import com.simiacryptus.skyenet.core.platform.User
 import com.simiacryptus.skyenet.webui.servlet.NewSessionServlet
 import com.simiacryptus.skyenet.webui.session.SocketManager
 import org.eclipse.jetty.servlet.DefaultServlet
@@ -18,7 +15,7 @@ import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory
 abstract class ChatServer(val resourceBase: String) {
 
     abstract val applicationName: String
-    open val dataStorage: DataStorage? = null
+    open val dataStorage: StorageInterface? = null
 
     inner class WebSocketHandler : JettyWebSocketServlet() {
         private val stateCache: MutableMap<Session, SocketManager> = mutableMapOf()
@@ -33,7 +30,7 @@ abstract class ChatServer(val resourceBase: String) {
                             if (stateCache.containsKey(session)) {
                                 stateCache[session]!!
                             } else {
-                                val user = authenticationManager.getUser(req.getCookie(AUTH_COOKIE))
+                                val user = authenticationManager.getUser(req.getCookie(AuthenticationInterface.AUTH_COOKIE))
                                 val sessionState = newSession(user, session)
                                 stateCache[session] = sessionState
                                 sessionState
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt
index de120c18..17e3c823 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt
@@ -3,15 +3,13 @@ package com.simiacryptus.skyenet.webui.servlet
 import com.simiacryptus.jopenai.util.JsonUtil
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
 import com.simiacryptus.skyenet.core.platform.ApplicationServices.authorizationManager
-import com.simiacryptus.skyenet.core.platform.AuthorizationManager
-import com.simiacryptus.skyenet.core.platform.AuthorizationManager.OperationType
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType
 import com.simiacryptus.skyenet.core.platform.Session
 import com.simiacryptus.skyenet.webui.application.ApplicationServer
 import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie
 import jakarta.servlet.http.HttpServlet
 import jakarta.servlet.http.HttpServletRequest
 import jakarta.servlet.http.HttpServletResponse
-import java.util.*
 
 class DeleteSessionServlet(
     private val server: ApplicationServer,
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt
index 8bf59072..8bb3bbe3 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt
@@ -1,8 +1,8 @@
 package com.simiacryptus.skyenet.webui.servlet
 
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.DataStorage
 import com.simiacryptus.skyenet.core.platform.Session
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import com.simiacryptus.skyenet.webui.application.ApplicationServer
 import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie
 import jakarta.servlet.http.HttpServlet
@@ -10,7 +10,7 @@ import jakarta.servlet.http.HttpServletRequest
 import jakarta.servlet.http.HttpServletResponse
 import java.io.File
 
-class FileServlet(val dataStorage: DataStorage) : HttpServlet() {
+class FileServlet(val dataStorage: StorageInterface) : HttpServlet() {
     override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
         val path = req.pathInfo ?: "/"
         val pathSegments = parsePath(path)
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/NewSessionServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/NewSessionServlet.kt
index 5ce2b952..b522a1a5 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/NewSessionServlet.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/NewSessionServlet.kt
@@ -1,13 +1,13 @@
 package com.simiacryptus.skyenet.webui.servlet
 
-import com.simiacryptus.skyenet.core.platform.DataStorage
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import jakarta.servlet.http.HttpServlet
 import jakarta.servlet.http.HttpServletRequest
 import jakarta.servlet.http.HttpServletResponse
 
 class NewSessionServlet : HttpServlet() {
     override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
-        val sessionId = DataStorage.newGlobalID()
+        val sessionId = StorageInterface.newGlobalID()
         resp.contentType = "text/plain"
         resp.status = HttpServletResponse.SC_OK
         resp.writer.write(sessionId.toString())
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/OAuthGoogle.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/OAuthGoogle.kt
index e258198a..50ee327d 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/OAuthGoogle.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/OAuthGoogle.kt
@@ -7,7 +7,7 @@ import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
 import com.google.api.client.json.gson.GsonFactory
 import com.google.api.services.oauth2.Oauth2
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.AuthenticationManager.Companion.AUTH_COOKIE
+import com.simiacryptus.skyenet.core.platform.AuthenticationInterface
 import com.simiacryptus.skyenet.core.platform.User
 import jakarta.servlet.*
 import jakarta.servlet.http.Cookie
@@ -95,7 +95,7 @@ open class OAuthGoogle(
                 )
                 ApplicationServices.authenticationManager.putUser(accessToken = sessionID, user = user)
                 log.info("User $user logged in with session $sessionID")
-                val sessionCookie = Cookie(AUTH_COOKIE, sessionID)
+                val sessionCookie = Cookie(AuthenticationInterface.AUTH_COOKIE, sessionID)
                 sessionCookie.path = "/"
                 sessionCookie.isHttpOnly = true
                 sessionCookie.secure = true
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionListServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionListServlet.kt
index 43f34670..2248fd2b 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionListServlet.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionListServlet.kt
@@ -2,7 +2,7 @@ package com.simiacryptus.skyenet.webui.servlet
 
 import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent
 import com.simiacryptus.skyenet.core.platform.ApplicationServices.authenticationManager
-import com.simiacryptus.skyenet.core.platform.DataStorage
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import com.simiacryptus.skyenet.webui.application.ApplicationServer
 import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie
 import jakarta.servlet.http.HttpServlet
@@ -11,7 +11,7 @@ import jakarta.servlet.http.HttpServletResponse
 import java.text.SimpleDateFormat
 
 class SessionListServlet(
-  private val dataStorage: DataStorage,
+  private val dataStorage: StorageInterface,
   private val prefix: String,
   private val applicationServer: ApplicationServer
 ) : HttpServlet() {
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt
index 871f7f4d..63a6b64d 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt
@@ -2,7 +2,7 @@ package com.simiacryptus.skyenet.webui.servlet
 
 import com.simiacryptus.jopenai.util.JsonUtil
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.UserSettingsManager.UserSettings
+import com.simiacryptus.skyenet.core.platform.UserSettingsInterface.UserSettings
 import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie
 import jakarta.servlet.http.HttpServlet
 import jakarta.servlet.http.HttpServletRequest
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/WelcomeServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/WelcomeServlet.kt
index 1b268547..5233fffa 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/WelcomeServlet.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/WelcomeServlet.kt
@@ -1,8 +1,8 @@
 package com.simiacryptus.skyenet.webui.servlet
 
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.AuthorizationManager
-import com.simiacryptus.skyenet.core.platform.DataStorage
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import com.simiacryptus.skyenet.core.platform.User
 import com.simiacryptus.skyenet.webui.application.ApplicationServer
 import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie
@@ -95,23 +95,23 @@ open class WelcomeServlet(private val parent : com.simiacryptus.skyenet.webui.ap
                 val canRun = ApplicationServices.authorizationManager.isAuthorized(
                     applicationClass = app.server.javaClass,
                     user = user,
-                    operationType = AuthorizationManager.OperationType.Write
+                    operationType = OperationType.Write
                 )
                 val canRead = ApplicationServices.authorizationManager.isAuthorized(
                     applicationClass = app.server.javaClass,
                     user = user,
-                    operationType = AuthorizationManager.OperationType.Read
+                    operationType = OperationType.Read
                 )
                 val canShare = ApplicationServices.authorizationManager.isAuthorized(
                     applicationClass = app.server.javaClass,
                     user = user,
-                    operationType = AuthorizationManager.OperationType.Share
+                    operationType = OperationType.Share
                 )
                 if (!canRead) return@joinToString ""
                 val newGlobalSessionLink =
-                    if (canShare) """<a class="new-session-link" href="${app.path}/#${DataStorage.newGlobalID()}">New Shared Session</a>""" else ""
+                    if (canShare) """<a class="new-session-link" href="${app.path}/#${StorageInterface.newGlobalID()}">New Shared Session</a>""" else ""
                 val newUserSessionLink =
-                    if (canRun) """<a class="new-session-link" href="${app.path}/#${DataStorage.newUserID()}">New Private Session</a>""" else ""
+                    if (canRun) """<a class="new-session-link" href="${app.path}/#${StorageInterface.newUserID()}">New Private Session</a>""" else ""
                 """
                         <a
                         <tr>
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/ZipServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/ZipServlet.kt
index 59f6a9ce..d0d2306d 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/ZipServlet.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/ZipServlet.kt
@@ -1,8 +1,8 @@
 package com.simiacryptus.skyenet.webui.servlet
 
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.DataStorage
 import com.simiacryptus.skyenet.core.platform.Session
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie
 import jakarta.servlet.http.HttpServlet
 import jakarta.servlet.http.HttpServletRequest
@@ -11,7 +11,7 @@ import java.io.File
 import java.util.zip.ZipEntry
 import java.util.zip.ZipOutputStream
 
-class ZipServlet(val dataStorage: DataStorage) : HttpServlet() {
+class ZipServlet(val dataStorage: StorageInterface) : HttpServlet() {
     override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
         val session = Session(req.getParameter("session"))
         val path = req.parameterMap.get("path")?.find { it.isNotBlank() } ?: "/"
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt
index dac82f4a..28d42ce3 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt
@@ -1,19 +1,20 @@
 package com.simiacryptus.skyenet.webui.session
 
-import com.google.common.util.concurrent.ListeningExecutorService
-import com.google.common.util.concurrent.MoreExecutors
-import com.simiacryptus.skyenet.core.platform.*
+import com.simiacryptus.skyenet.core.platform.ApplicationServices
 import com.simiacryptus.skyenet.core.platform.ApplicationServices.clientManager
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType
+import com.simiacryptus.skyenet.core.platform.Session
+import com.simiacryptus.skyenet.core.platform.StorageInterface
+import com.simiacryptus.skyenet.core.platform.User
 import com.simiacryptus.skyenet.webui.chat.ChatServer
 import com.simiacryptus.skyenet.webui.chat.ChatSocket
 import com.simiacryptus.skyenet.webui.util.MarkdownUtil
 import org.slf4j.LoggerFactory
-import java.util.concurrent.Executors
 import java.util.concurrent.atomic.AtomicInteger
 
 abstract class SocketManagerBase(
     protected val session: Session,
-    protected val dataStorage: DataStorage?,
+    protected val dataStorage: StorageInterface?,
     protected val user: User? = null,
     private val messageStates: LinkedHashMap<String, String> = dataStorage?.getMessages(
         user, session
@@ -117,7 +118,7 @@ abstract class SocketManagerBase(
     open fun canWrite(user: User?) = ApplicationServices.authorizationManager.isAuthorized(
         applicationClass = applicationClass,
         user = user,
-        operationType = AuthorizationManager.OperationType.Write
+        operationType = OperationType.Write
     )
 
     protected open fun onCmd(
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt
index f8fba397..e65f8682 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt
@@ -3,7 +3,7 @@ package com.simiacryptus.skyenet.webui.test
 import com.simiacryptus.jopenai.API
 import com.simiacryptus.skyenet.core.actors.CodingActor
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.AuthorizationManager
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType
 import com.simiacryptus.skyenet.core.platform.Session
 import com.simiacryptus.skyenet.core.platform.User
 import com.simiacryptus.skyenet.webui.application.ApplicationInterface
@@ -34,7 +34,7 @@ open class CodingActorTestApp(
             val canPlay = ApplicationServices.authorizationManager.isAuthorized(
                 this::class.java,
                 user,
-                AuthorizationManager.OperationType.Execute
+              OperationType.Execute
             )
             val playLink = if (!canPlay) "" else {
                 ui.hrefLink("▶", "href-link play-button") {
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/TensorflowProjector.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/TensorflowProjector.kt
index 3ea45a77..934ee259 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/TensorflowProjector.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/TensorflowProjector.kt
@@ -4,14 +4,14 @@ import com.simiacryptus.jopenai.API
 import com.simiacryptus.jopenai.OpenAIClient
 import com.simiacryptus.jopenai.models.EmbeddingModels
 import com.simiacryptus.jopenai.util.JsonUtil
-import com.simiacryptus.skyenet.core.platform.DataStorage
 import com.simiacryptus.skyenet.core.platform.Session
+import com.simiacryptus.skyenet.core.platform.StorageInterface
 import com.simiacryptus.skyenet.core.platform.User
 import com.simiacryptus.skyenet.webui.application.ApplicationInterface
 
 class TensorflowProjector(
     val api: API,
-    val dataStorage: DataStorage,
+    val dataStorage: StorageInterface,
     val sessionID: Session,
     val appPath: String,
     val host: String,
diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/webui/ActorTestAppServer.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/webui/ActorTestAppServer.kt
index 8056fb55..fba4db69 100644
--- a/webui/src/test/kotlin/com/simiacryptus/skyenet/webui/ActorTestAppServer.kt
+++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/webui/ActorTestAppServer.kt
@@ -6,8 +6,8 @@ import com.simiacryptus.skyenet.core.actors.ImageActor
 import com.simiacryptus.skyenet.core.actors.ParsedActor
 import com.simiacryptus.skyenet.core.actors.SimpleActor
 import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.AuthenticationManager
-import com.simiacryptus.skyenet.core.platform.AuthorizationManager
+import com.simiacryptus.skyenet.core.platform.AuthenticationInterface
+import com.simiacryptus.skyenet.core.platform.AuthorizationInterface
 import com.simiacryptus.skyenet.core.platform.User
 import com.simiacryptus.skyenet.groovy.GroovyInterpreter
 import com.simiacryptus.skyenet.kotlin.KotlinInterpreter
@@ -51,16 +51,17 @@ object ActorTestAppServer : com.simiacryptus.skyenet.webui.application.Applicati
             "Test User",
             ""
         )
-        ApplicationServices.authenticationManager = object : AuthenticationManager() {
+        ApplicationServices.authenticationManager = object : AuthenticationInterface {
             override fun getUser(accessToken: String?) = mockUser
             override fun containsUser(value: String) = true
             override fun putUser(accessToken: String, user: User) = throw UnsupportedOperationException()
+            override fun logout(accessToken: String, user: User) {}
         }
-        ApplicationServices.authorizationManager = object : AuthorizationManager() {
+        ApplicationServices.authorizationManager = object : AuthorizationInterface {
             override fun isAuthorized(
                 applicationClass: Class<*>?,
                 user: User?,
-                operationType: OperationType
+                operationType: AuthorizationInterface.OperationType
             ): Boolean = true
         }
         super._main(args)