- Dependencies
- ✅ Ktor Server added to
build.gradle.kts - ✅ ZXing library for QR code generation
- ✅ Ktor Server added to
- Network Config - Local network cleartext allowed
- Models -
LocalSyncModels.ktwith all DTOs - Server -
LocalSyncServer.ktwith REST API endpoints - Repository -
LocalSyncRepository.ktfor server management - UI Components
- ✅
LocalSyncSection.ktwith Start/Stop & QR button - ✅
QRCodeDialog.ktfor showing QR code
- ✅
- Docs -
VIEW_ONLY_SETUP.mdandQUICKSTART.md
- React App - View-Only interface (no message sending)
- Features
- ✅ Search conversations by title
- ✅ Memory viewer (🧠)
- ✅ Export to MD/TXT
- ✅ Copy messages to clipboard
- ✅ Modern responsive UI
- ✅ Connection status indicator
- Dependencies - All packages in
package.json - Docs -
web-client/README.md
// In SettingsViewModel.kt
// Add to constructor
private val localSyncRepository: LocalSyncRepository
// Add to UiState
data class UiState(
// ... existing fields ...
val localSyncServerStatus: ServerStatus? = null
)
// Add methods
fun startLocalSyncServer() {
viewModelScope.launch {
val deviceId = apiKeyRepository.getDeviceId() // or generate one
val started = localSyncRepository.startServer(deviceId) // suspend function
if (started) {
updateLocalSyncStatus()
}
}
}
fun stopLocalSyncServer() {
viewModelScope.launch {
localSyncRepository.stopServer()
_uiState.update { it.copy(localSyncServerStatus = null) }
}
}
private suspend fun updateLocalSyncStatus() {
val status = localSyncRepository.getServerStatus()
_uiState.update { it.copy(localSyncServerStatus = status) }
}
// In init or onResume
viewModelScope.launch {
localSyncRepository.serverStatus.collect { status ->
_uiState.update { it.copy(localSyncServerStatus = status) }
}
}// In SettingsScreen content, add after CloudSyncSection:
Spacer(modifier = Modifier.height(16.dp))
// Local Network Sync Section
LocalSyncSection(
serverStatus = uiState.localSyncServerStatus,
onStartServer = { viewModel.startLocalSyncServer() },
onStopServer = { viewModel.stopLocalSyncServer() }
)// In ApiKeyRepository.kt
private const val KEY_DEVICE_ID = "device_id"
fun getDeviceId(): String {
var deviceId = encryptedPrefs.getString(KEY_DEVICE_ID, null)
if (deviceId == null) {
deviceId = UUID.randomUUID().toString()
encryptedPrefs.edit().putString(KEY_DEVICE_ID, deviceId).apply()
}
return deviceId
}./gradlew assembleDebug- Open Settings → Local Network Sync
- Click "Start Server"
- Note the IP address shown
# Make script executable
chmod +x test_sync.sh
# Run tests (replace with your phone's IP)
./test_sync.sh 192.168.1.100🔍 Testing YourOwnAI Local Sync Server
Server: http://192.168.1.100:8765
1️⃣ Health Check
✅ SUCCESS - YourOwnAI Local Sync Server
2️⃣ Server Status
✅ SUCCESS
{
"isRunning": true,
"deviceInfo": {...},
"totalConversations": 23,
"totalMemories": 13
}
3️⃣ Get Conversations
✅ SUCCESS - Found 23 conversations
...
- Settings → About Phone → Status → IP address
- Or: Settings → Wi-Fi → [Your Network] → IP address
# From Desktop, scan local network
nmap -sn 192.168.1.0/24 | grep "192.168"See README_LOCAL_SYNC.md for Desktop app implementation guide.
Choose one:
- Compose Desktop (80% code reuse)
- Tauri (fast prototyping)
- Electron (web tech)
Current Status: Backend Ready ✅
Next: UI Integration (15 minutes) → Testing → Desktop Client