On this page
- Define the backup boundary first
- Keep credentials out by default
- Separate configuration from secrets
- Protected custom headers are credentials too
- The backup format should be versioned
- Treat restore input as untrusted
- Validate structure before content
- Enforce size and count limits
- Defend against archive path traversal
- Do not restore arbitrary absolute paths
- Validate IDs and references
- Merge and replace are different operations
- Merge
- Replace
- Replace restore should not erase credentials accidentally unless intended
- Never match credentials by secret value
- Identifier collision policy matters
- Restore should be transactional
- Indexes should be rebuilt from trusted source data
- Tool history deserves special treatment
- Restored approvals should not authorize future execution
- Remote resources may no longer exist
- Backups can contain prompt injection text
- Encrypting backups is optional, but understand what it solves
- File-level platform protection still matters
- Integrity checks detect corruption, not necessarily authenticity
- If you add signatures, define the trust root
- Backup creation should be deterministic enough to test
- Test round trips across versions
- Restore should produce a plan before destructive action
- Deletion and backups have different scopes
- Import/export provider configuration separately when useful
- A secure backup lifecycle
- Where BYOKchat fits
- Further reading
A backup concentrates a large amount of user data into one portable artifact.
That makes it valuable for recovery—and valuable to an attacker.
A local AI chat backup may contain:
- conversations;
- projects;
- attachments;
- tool history;
- provider configuration;
- usage history;
- library files;
- local analytics.
The safest design starts with two rules:
Backups are data, not authority.
and:
Credentials should be excluded unless the product has a very deliberate encrypted-secret export design.
Define the backup boundary first
A backup should clearly document what it contains.
Example:
Included:
- conversations
- messages
- projects
- project instructions
- attachments
- library records
- provider connection metadata
- model preferences
- tool history
Excluded:
- API keys
- OAuth access tokens
- refresh tokens
- protected custom headers
- Keychain-only secrets
That boundary should be enforced by the serializer, not just described in UI copy.
Keep credentials out by default
A restored provider connection can come back as:
Anthropic — credential required
instead of embedding the API key inside the archive.
This is a strong tradeoff:
- backups remain useful;
- credential rotation stays independent;
- users can share/move archives with lower risk;
- a stolen backup is less immediately exploitable.
See How Mobile Apps Should Store AI API Keys.
Separate configuration from secrets
A provider record might serialize as:
{
"id": "connection-123",
"kind": "anthropic",
"display_name": "Personal Anthropic",
"base_url": "https://api.anthropic.com",
"credential_present": true,
"credential": null
}
The credential_present hint can help the UI explain what needs to be reconfigured, while the secret itself remains outside the archive.
Protected custom headers are credentials too
If the user configures:
Authorization
X-API-Key
X-Internal-Token
as protected headers, those values should follow the same exclusion rule as the main API key.
Do not accidentally serialize them because they live inside a generic headers dictionary.
The backup format should be versioned
A durable archive needs an explicit schema version:
{
"format": "byokchat-backup",
"version": 3,
"created_at": "2026-09-04T02:00:00Z"
}
Versioning lets restore code distinguish:
old field missing
old field renamed
new field unsupported
migration required
archive too new
Do not infer format only from filenames.
Treat restore input as untrusted
Even if your app originally generated the archive, the file may have been:
- edited;
- corrupted;
- truncated;
- generated by an older buggy version;
- intentionally crafted by an attacker.
Restore code should use the same mindset as network parsing.
Validate before mutating local state.
Validate structure before content
A safe restore pipeline can look like:
Do not start writing database rows while the archive is only half-parsed.
Enforce size and count limits
A malformed backup can try to allocate excessive resources.
Set reasonable limits for:
archive size
number of conversations
messages per conversation
attachment count
attachment size
string lengths
nested metadata depth
The limits should be generous enough for real users but finite.
Defend against archive path traversal
If the backup is a ZIP-like container with attachment paths, never trust filenames directly.
Dangerous entries can look like:
../../Library/something
/absolute/path
Extract only into a controlled temporary directory and verify every resolved path stays inside it.
Prefer manifest IDs over user-controlled filesystem paths.
Do not restore arbitrary absolute paths
A record such as:
{
"attachment_path": "/Users/alice/Documents/private.pdf"
}
should not cause restore code to read or overwrite that path.
Backups should carry file content or app-owned relative object IDs, not authority over arbitrary filesystem locations.
Validate IDs and references
A backup can contain broken relationships:
message references missing conversation
attachment references missing blob
project references unknown provider
multiple objects reuse same supposedly unique ID
Build an import graph and reject or repair invalid references deterministically.
Avoid silently creating surprising state.
Merge and replace are different operations
A good restore UI can distinguish:
Merge
keep existing data
add imported data
resolve identifier collisions
Replace
remove current app-managed data
then restore archive
These have very different consequences.
Do not label both simply “Restore.”
Replace restore should not erase credentials accidentally unless intended
Suppose the archive excludes credentials.
If “Replace” wipes all Keychain items first, the user may lose working provider keys even though the backup cannot restore them.
Decide the policy explicitly.
One reasonable model:
replace content/configuration
preserve credentials for matching local connections when safely identifiable
Another:
full reset + restore, credentials must be re-entered
Either can be valid, but the UI must be clear.
Never match credentials by secret value
If you preserve existing credentials across restore, map them using app-owned stable connection identity or deliberate user choice.
Do not hash an API key and store the hash in the backup just to reconnect it later.
That creates a credential-derived identifier with little benefit.
Identifier collision policy matters
A merge restore can encounter the same UUID as an existing local object.
Possible strategies:
identical object -> skip
same ID but different content -> remap imported ID
newer version -> explicit conflict resolution
Avoid overwriting silently unless the format defines that behavior.
Restore should be transactional
A failure halfway through import should not leave:
half the conversations restored
some attachments missing
provider config partly replaced
indexes inconsistent
Prefer:
parse
validate
stage
transactionally commit
rebuild indexes
If files and database cannot share one transaction, use a staged import directory and an operation journal.
Indexes should be rebuilt from trusted source data
Do not blindly import a search index or embedding database when it can be rebuilt from primary records.
Derived indexes can be:
- stale;
- corrupt;
- version-specific;
- maliciously crafted.
Restore primary data, then regenerate derived state.
Tool history deserves special treatment
Tool records may contain:
- arguments;
- results;
- external IDs;
- approval decisions.
Restoring history is useful for conversation continuity.
Restoring authorization is different.
Do not let an old archive silently turn:
Ask
into:
Always Allow
without validating whether tool permission settings should be imported at all.
Backups should not become privilege-escalation packages.
Restored approvals should not authorize future execution
An approval is usually tied to:
specific operation
specific normalized arguments
specific run/context
Importing historical approval cards is fine for display.
Treat them as history, not reusable permission tokens.
Remote resources may no longer exist
A restored conversation may reference:
- provider-uploaded file IDs;
- background response IDs;
- MCP task IDs;
- remote document IDs.
Those resources may have expired or been deleted.
The app should preserve historical metadata but gracefully mark remote links unavailable rather than assuming restore recreates provider state.
Backups can contain prompt injection text
A user conversation may legitimately include malicious-looking text because the user was analyzing it.
That content should not execute during restore.
Restore code should never interpret message text as commands.
Similarly, restored tool results and Markdown should remain inert data until normal rendering/security policy handles them.
Encrypting backups is optional, but understand what it solves
Archive encryption can protect confidentiality if the backup is copied or stored somewhere untrusted.
But encryption introduces:
password/key management
recovery UX
key derivation choices
format compatibility
lost-password scenarios
Do not invent custom cryptography casually.
If unencrypted backups are supported, clearly tell users that the archive contains their chat data and should be stored accordingly.
File-level platform protection still matters
Before export, backup staging files may temporarily exist inside the app sandbox.
Use app-owned protected storage and remove staging files after success/failure.
Do not leave duplicate archives in temporary directories indefinitely.
Integrity checks detect corruption, not necessarily authenticity
A checksum can tell you that bytes changed relative to a manifest.
It does not prove who created the archive unless the manifest is authenticated with a trusted key.
For ordinary user backups, checksums are still useful for corruption detection.
Do not label an unauthenticated checksum as a signature.
If you add signatures, define the trust root
A signed archive raises questions:
Who owns the signing key?
Is it per-installation or app-wide?
Can another device verify it?
What happens after reinstall?
Does signing imply the archive is safe to restore?
Even a valid signature does not replace schema validation.
Signed malicious content is still malicious if the signer is compromised.
Backup creation should be deterministic enough to test
Useful invariants:
no Keychain secret appears
no protected header value appears
all attachment references resolve
schema version present
manifest counts match payloads
restore of backup recreates expected primary records
Use sentinel secrets and scan the raw archive bytes.
Test round trips across versions
At minimum:
current -> current
previous version -> current
archive missing optional fields
archive with unknown future fields
archive version too new
corrupt manifest
missing attachment
ID collision
merge restore
replace restore
For long-lived apps, keep a small fixture archive from each supported schema generation.
Restore should produce a plan before destructive action
Before committing, the UI can show:
42 conversations
3 projects
18 files
4 provider configurations
0 credentials
For replace mode:
This will replace current conversations and projects.
Provider credentials are not included in the backup.
That is much safer than a generic confirmation dialog.
Deletion and backups have different scopes
Deleting data from the app does not delete old archives the user saved in Files, Drive, or another location.
The app should be explicit about that boundary.
See How to Delete AI App Data Correctly.
Import/export provider configuration separately when useful
Some users want to move provider endpoints without moving chat history.
A small secret-free provider-config export can reduce the need to share a full backup.
For example:
provider type
base URL
model selection
safe headers
protected-header names without values
Credentials are re-entered on the destination device.
A secure backup lifecycle
Credentials remain outside this flow unless the product deliberately supports a separate secure credential-transfer mechanism.
Where BYOKchat fits
A local-first BYOK client benefits from portable, versioned backups because the user’s conversations and projects live primarily on-device.
The safest backup contract preserves user-created content, attachments, project/library state, tool history, and provider configuration while excluding API keys and protected headers.
Restore should treat the archive as untrusted input, validate the whole import before mutation, keep historical tool approvals non-authoritative, and rebuild derived indexes rather than trusting them.