DEV Community

Roronoa
Roronoa

Posted on

Keep Prompt Caches Out of Device Backup on Your First Mobile AI PR

You join a mobile AI repo on day one and install the debug build on a Pixel 8 running Android 15. The app stays in the foreground with a warm local embedding cache and no network spinner visible. You then start a system backup before anyone explains which on-device files are allowed to leave. Treat that sequence as a proposed first-hour walkthrough, not as a measured result from a device farm.

Why backup is the onboarding trap for on-device AI

Junior engineers often hunt for the happy-path inference call and miss the files that inference leaves behind. On-device embeddings, prompt transcripts, and downloaded model shards usually land in app-private storage that backup tools still visit. A restore onto a replacement phone can revive those files after you thought a rollback had wiped them. Your first job is to name every AI artifact on disk before you open a pull request.

Record the environment before you touch Settings, Finder, or Android Backup.

  • Device and OS: write the exact phone and build, for example Pixel 8 / Android 15 or iPhone 15 / iOS 18
  • Framework: copy React Native, Flutter, Kotlin, or Swift versions from the lockfile, not from memory
  • Network: Wi-Fi with backup enabled, then airplane mode for the recovery pass
  • Permissions: only those the debug build actually requests, which may be none for a text summarizer
  • Power: leave the device unplugged so backup is not confused with a charging-only job
  • App state: foreground with a warm cache, then force-stop, then uninstall

Those fields are what a reviewer needs when a cache returns after rollback. Skip invented battery numbers, because a single unplugged backup is not an energy benchmark.

First hour: map one prompt onto real files

Do not start by asking an agent to add a new model path or a cloud fallback. Force one ordinary utterance through the current build while the process stays in the foreground. Type a non-secret phrase such as calendar reminder for Tuesday and wait until the local pipeline finishes. Then inspect storage before the app is backgrounded, killed, or backed up.

Proposed Android inspection commands, only against a debug package you installed yourself:

# Confirm the debug package
adb shell pm path com.example.mobileai

# List likely AI artifacts after one utterance
adb shell run-as com.example.mobileai ls -la files
adb shell run-as com.example.mobileai ls -la files/ai_cache
adb shell run-as com.example.mobileai ls -la databases

# Hash anything that looks like an embedding index or transcript
adb shell run-as com.example.mobileai sha256sum files/ai_cache/*
Enter fullscreen mode Exit fullscreen mode

Proposed iOS inspection on a personal device with a development profile:

# Xcode → Window → Devices and Simulators → Installed Apps → Download Container
unzip -l AppData.xcappdata
find AppData.xcappdata -iname '*embed*' -o -iname '*prompt*' -o -iname '*.gguf'
Enter fullscreen mode Exit fullscreen mode

Write down three paths before you discuss architecture. You want the model shard, the embedding or sqlite cache, and any rolling transcript. If a path is missing, the feature is either fully remote or the cache sits in a surprise directory such as cache/ instead of files/.

Android Auto Backup treats those domains differently, so the directory name is part of the privacy review. A one-page map beats a redesign on day one:

  1. User text or a short recording enters the input field.
  2. Bytes hit RAM, then a file, then optionally a network client.
  3. A retry queue or embedding index may keep a second copy.
  4. Google backup, iCloud, or a device-to-device transfer may keep a third copy.

If step 4 exists for prompts or embeddings, your first PR is a storage fix, not a model swap. Cross-platform wrappers do not change that rule, because Flutter and React Native still land on OS backup policy.

First PR: exclude the sensitive files from OS backup

The smallest honest PR on a mobile AI repo often changes backup rules rather than prompts. You want public model weights and any user-derived cache excluded from cloud backup. You can still restore crash logs that do not contain utterances. Label the snippets below as proposed defaults, then confirm them against current platform docs before you merge.

Primary references to keep next to the PR:

Android: backup_rules.xml

<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
    <cloud-backup>
        <exclude domain="file" path="ai_cache/"/>
        <exclude domain="file" path="models/"/>
        <exclude domain="database" path="prompt_log.db"/>
        <include domain="file" path="crash_reports/"/>
    </cloud-backup>
    <device-transfer>
        <exclude domain="file" path="ai_cache/"/>
        <exclude domain="file" path="models/"/>
        <exclude domain="database" path="prompt_log.db"/>
    </device-transfer>
</data-extraction-rules>
Enter fullscreen mode Exit fullscreen mode

Wire it in the manifest only after you confirm the app uses the modern backup schema:

<application
    android:dataExtractionRules="@xml/backup_rules"
    android:allowBackup="true">
Enter fullscreen mode Exit fullscreen mode

allowBackup="false" is a blunt option that also drops useful non-secret state. Prefer explicit excludes when the rest of the app still needs restore. If the cache is under cache/ instead of files/, update the domain and path so the rule actually matches.

iOS: mark files after you create them

import Foundation

func excludeFromBackup(url: URL) throws {
    var values = URLResourceValues()
    values.isExcludedFromBackup = true
    var fileURL = url
    try fileURL.setResourceValues(values)
}

// Proposed call sites, not a drop-in SDK:
// 1) after downloading a public model shard
// 2) after opening the embedding sqlite file
// 3) after rotating a local transcript
Enter fullscreen mode Exit fullscreen mode

Flutter and React Native still need those OS hooks. A Dart file from getApplicationSupportDirectory() is not excluded until something sets the iOS resource value. Android still needs the XML, because Dart cannot invent a backup domain the OS does not honor.

Proposed Flutter sketch, labeled as unexecuted glue:

import 'dart:io';
import 'package:flutter/services.dart';

const _backup = MethodChannel('ai_cache/backup');

Future<void> excludeAiCache(File file) async {
  if (Platform.isIOS) {
    await _backup.invokeMethod('excludeFromBackup', {'path': file.path});
  }
  // On Android, rely on dataExtractionRules, not this channel.
}
Enter fullscreen mode Exit fullscreen mode

The native iOS side of that channel should call the same isExcludedFromBackup helper. Do not treat the Dart file as proof the flag is set; download the container and read the resource value.

First rollback: uninstall, restore, and watch what returns

A rollback is not git revert alone. On a phone, rollback also means the user deleted the app, restored from backup, or accepted an OS migration. Your proposed rollback test should answer three questions in order.

  1. After uninstall, are model shards and prompt caches gone from app-private storage?
  2. After a cloud backup restore, do those files reappear without a new utterance?
  3. After restore, does the feature recover by re-downloading public weights only, never by replaying user text?

Proposed Android sequence for a personal debug device:

# Capture hashes while the cache is warm
adb shell run-as com.example.mobileai sha256sum files/ai_cache/*

# Uninstall after the backup job finishes in system UI
adb uninstall com.example.mobileai

# Restore through system UI, reinstall the same debug APK, then compare
adb shell run-as com.example.mobileai ls -la files/ai_cache
adb shell run-as com.example.mobileai sha256sum files/ai_cache/*
Enter fullscreen mode Exit fullscreen mode

Expected observations if the PR is correct: ai_cache is empty or missing, and no prior utterance is searchable. The public model may re-download, which is recovery, not a leak. Expected observations if the PR is wrong: the same sha256 hashes return without a new input. Do not treat those sentences as lab numbers; they are pass/fail signals you record on your own device.

On iOS, download the container again after restore and repeat the find search. A file that still has isExcludedFromBackup unset is a defect even if the UI looks healthy. If the summarizer silently shows yesterday’s sentence, the rollback failed, even when git status is clean.

A first-PR decision table

Artifact on disk Survives uninstall? Allowed in cloud backup? First PR action
Public model shard No No Exclude; re-download on recovery
Embedding index from user text No No Exclude; rebuild after a new utterance
Prompt / transcript sqlite No No Exclude, or stop writing it
Crash reports without utterances No Maybe Include only if redacted
Auth tokens No No Use the platform keystore, not files

If your repo already writes transcripts for debugging, gate that file behind a debug flag and the backup exclude list. A junior PR that only adds a prettier prompt is not a lifecycle fix.

Where a scratch coding workspace fits

You do not need a paid GPU to write this pull request. You need a place to draft the backup XML, the iOS helper, and a short test plan while the device is in your hand.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

MonkeyCode’s free model access and free server option can host that drafting work and a tiny public model-manifest JSON for re-download tests. Keep the manifest free of user prompts, utterance hashes, and anything that belongs in ai_cache. Treat the workspace as a scratch pad for the first PR, not as a production inference cluster.

A junior-friendly manifest for the recovery path can look like this:

{
  "model_id": "local-summarizer-public",
  "sha256": "replace-with-the-public-weight-hash",
  "uri": "https://example.invalid/models/summarizer.bin",
  "contains_user_data": false
}
Enter fullscreen mode Exit fullscreen mode

Host only public weights or a stub file. Never upload the on-device prompt log to that server, even when the server is free.

Limitations and who should skip this

This approach does not replace a security review, MDM policy, or a formal threat model. It will not help if the model runs entirely in a browser tab. It also will not help if your only AI call is a stateless HTTPS POST with no local cache. Work-profile devices, Play backup exceptions, and iCloud behavior vary by OS version, so copy versions from the device.

Skip this workflow when you do not have a physical device you are allowed to uninstall. Emulators often fake backup domains, and those fakes are not evidence. Skip it when the cache lives on a server you do not control, because mobile backup rules will not be the leak. Do not publish battery figures from the backup run; radio state and file size dominate, and one pass is not a benchmark.

Ask for comparable evidence

If you run this first-hour map on a real phone, report the device, OS, framework versions, and the exact transition. Name whether you went warm cache → backup → uninstall → restore. Say whether the cache recovered, the model re-downloaded, or the files silently reappeared. That is the signal a mobile AI reviewer can use on the next onboarding.

Top comments (0)