BYOKchat Blog

Backup and Restore Security for Local AI Chats

Design secure local AI chat backups with secret exclusion, versioned schemas, integrity checks, safe merge/replace restore, untrusted-input validation, and predictable deletion behavior.

· 6 min read

On this page
  1. Define the backup boundary first
  2. Keep credentials out by default
  3. Separate configuration from secrets
  4. Protected custom headers are credentials too
  5. The backup format should be versioned
  6. Treat restore input as untrusted
  7. Validate structure before content
  8. Enforce size and count limits
  9. Defend against archive path traversal
  10. Do not restore arbitrary absolute paths
  11. Validate IDs and references
  12. Merge and replace are different operations
  13. Merge
  14. Replace
  15. Replace restore should not erase credentials accidentally unless intended
  16. Never match credentials by secret value
  17. Identifier collision policy matters
  18. Restore should be transactional
  19. Indexes should be rebuilt from trusted source data
  20. Tool history deserves special treatment
  21. Restored approvals should not authorize future execution
  22. Remote resources may no longer exist
  23. Backups can contain prompt injection text
  24. Encrypting backups is optional, but understand what it solves
  25. File-level platform protection still matters
  26. Integrity checks detect corruption, not necessarily authenticity
  27. If you add signatures, define the trust root
  28. Backup creation should be deterministic enough to test
  29. Test round trips across versions
  30. Restore should produce a plan before destructive action
  31. Deletion and backups have different scopes
  32. Import/export provider configuration separately when useful
  33. A secure backup lifecycle
  34. Where BYOKchat fits
  35. 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:

Diagram illustrating the surrounding section

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

Diagram illustrating the surrounding section

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.

Further reading

Keep reading