diff --git a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/FileService.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/FileService.kt
new file mode 100644
index 000000000..18d6b17bf
--- /dev/null
+++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/FileService.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2026 Meshtastic LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.meshtastic.core.repository
+
+import okio.BufferedSink
+import okio.BufferedSource
+import org.meshtastic.core.common.util.MeshtasticUri
+
+/**
+ * Abstracts file system operations (like reading from or writing to URIs)
+ * so that ViewModels can remain platform-independent.
+ */
+interface FileService {
+ /**
+ * Opens a file or URI for writing and provides a [BufferedSink].
+ * The sink is automatically closed after [block] execution.
+ * Returns true if successful, false otherwise.
+ */
+ suspend fun write(uri: MeshtasticUri, block: suspend (BufferedSink) -> Unit): Boolean
+
+ /**
+ * Opens a file or URI for reading and provides a [BufferedSource].
+ * The source is automatically closed after [block] execution.
+ * Returns true if successful, false otherwise.
+ */
+ suspend fun read(uri: MeshtasticUri, block: suspend (BufferedSource) -> Unit): Boolean
+}
diff --git a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationService.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationService.kt
new file mode 100644
index 000000000..28be8b8a3
--- /dev/null
+++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationService.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2026 Meshtastic LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.meshtastic.core.repository
+
+/**
+ * Abstracts high-level location requests (such as one-off current location)
+ * that may require platform-specific permission checks or hardware interactions.
+ */
+interface LocationService {
+ /**
+ * Requests the current location, if permissions and hardware allow.
+ * Returns null if unavailable or if permissions are not granted.
+ */
+ suspend fun getCurrentLocation(): Location?
+}
diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidFileService.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidFileService.kt
new file mode 100644
index 000000000..e24d2f21e
--- /dev/null
+++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidFileService.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2026 Meshtastic LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.meshtastic.core.service
+
+import android.app.Application
+import co.touchlab.kermit.Logger
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import okio.BufferedSink
+import okio.BufferedSource
+import okio.buffer
+import okio.sink
+import okio.source
+import org.koin.core.annotation.Single
+import org.meshtastic.core.common.util.MeshtasticUri
+import org.meshtastic.core.common.util.toAndroidUri
+import org.meshtastic.core.repository.FileService
+
+@Single
+class AndroidFileService(private val context: Application) : FileService {
+ override suspend fun write(uri: MeshtasticUri, block: suspend (BufferedSink) -> Unit): Boolean =
+ withContext(Dispatchers.IO) {
+ try {
+ context.contentResolver.openFileDescriptor(uri.toAndroidUri(), "wt")?.use { pfd ->
+ java.io.FileOutputStream(pfd.fileDescriptor).sink().buffer().use { sink ->
+ block(sink)
+ }
+ }
+ true
+ } catch (e: Exception) {
+ Logger.e(e) { "Failed to write to URI: $uri" }
+ false
+ }
+ }
+
+ override suspend fun read(uri: MeshtasticUri, block: suspend (BufferedSource) -> Unit): Boolean =
+ withContext(Dispatchers.IO) {
+ try {
+ context.contentResolver.openInputStream(uri.toAndroidUri())?.use { inputStream ->
+ inputStream.source().buffer().use { source ->
+ block(source)
+ }
+ }
+ true
+ } catch (e: Exception) {
+ Logger.e(e) { "Failed to read from URI: $uri" }
+ false
+ }
+ }
+}
diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidLocationService.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidLocationService.kt
new file mode 100644
index 000000000..70ed92287
--- /dev/null
+++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidLocationService.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2026 Meshtastic LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.meshtastic.core.service
+
+import android.Manifest
+import android.app.Application
+import android.content.pm.PackageManager
+import androidx.core.content.ContextCompat
+import kotlinx.coroutines.flow.firstOrNull
+import org.koin.core.annotation.Single
+import org.meshtastic.core.repository.Location
+import org.meshtastic.core.repository.LocationRepository
+import org.meshtastic.core.repository.LocationService
+
+@Single
+class AndroidLocationService(
+ private val context: Application,
+ private val locationRepository: LocationRepository
+) : LocationService {
+
+ override suspend fun getCurrentLocation(): Location? {
+ val hasPermission = ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED
+
+ if (!hasPermission) {
+ return null
+ }
+
+ return locationRepository.getLocations().firstOrNull()
+ }
+}
diff --git a/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmFileService.kt b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmFileService.kt
new file mode 100644
index 000000000..a611330bb
--- /dev/null
+++ b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmFileService.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2026 Meshtastic LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.meshtastic.core.service
+
+import co.touchlab.kermit.Logger
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import okio.BufferedSink
+import okio.BufferedSource
+import okio.buffer
+import okio.sink
+import okio.source
+import org.koin.core.annotation.Single
+import org.meshtastic.core.common.util.MeshtasticUri
+import org.meshtastic.core.repository.FileService
+import java.io.File
+
+@Single
+class JvmFileService : FileService {
+ override suspend fun write(uri: MeshtasticUri, block: suspend (BufferedSink) -> Unit): Boolean =
+ withContext(Dispatchers.IO) {
+ try {
+ // Treat uriString as a local file path
+ val file = File(uri.uriString)
+ file.parentFile?.mkdirs()
+ file.sink().buffer().use { sink ->
+ block(sink)
+ }
+ true
+ } catch (e: Exception) {
+ Logger.e(e) { "Failed to write to URI: $uri" }
+ false
+ }
+ }
+
+ override suspend fun read(uri: MeshtasticUri, block: suspend (BufferedSource) -> Unit): Boolean =
+ withContext(Dispatchers.IO) {
+ try {
+ val file = File(uri.uriString)
+ file.source().buffer().use { source ->
+ block(source)
+ }
+ true
+ } catch (e: Exception) {
+ Logger.e(e) { "Failed to read from URI: $uri" }
+ false
+ }
+ }
+}
diff --git a/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmLocationService.kt b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmLocationService.kt
new file mode 100644
index 000000000..7e0124dab
--- /dev/null
+++ b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmLocationService.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2026 Meshtastic LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.meshtastic.core.service
+
+import org.koin.core.annotation.Single
+import org.meshtastic.core.repository.Location
+import org.meshtastic.core.repository.LocationService
+
+@Single
+class JvmLocationService : LocationService {
+ override suspend fun getCurrentLocation(): Location? {
+ // Location services on JVM/Desktop are currently stubbed
+ return null
+ }
+}