Skip to content

feat: duck.ai native storage bridge and debugging tools#8222

Merged
aitorvs merged 6 commits intodevelopfrom
feature/aitorvs/duck-ai-native-storage
Apr 14, 2026
Merged

feat: duck.ai native storage bridge and debugging tools#8222
aitorvs merged 6 commits intodevelopfrom
feature/aitorvs/duck-ai-native-storage

Conversation

@aitorvs
Copy link
Copy Markdown
Collaborator

@aitorvs aitorvs commented Apr 8, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/task/1213883846466540

Description

Duck.ai currently stores all its data — chat history, settings, and uploaded images — in the browser's WebView storage (localStorage and IndexedDB). This means the fire button can't clear duck.ai data, and there's no way to inspect or manage it from the native side.

This PR introduces a native Android storage bridge that duck.ai can use instead of WebView storage:

  • Chat history and settings are stored in a Room database, surviving WebView clears and making them accessible to native features like sync.
  • Uploaded image files are stored on-disk with metadata (filename, MIME type, chat association) tracked in a separate Room table. The file listing reads from the DB rather than parsing file contents, making it fast even with hundreds of files.
  • A migration path is provided so existing WebView data can be moved to native storage on first launch.
  • An internal debug server (available in internal builds only) exposes a local HTTP interface to inspect and manage the stored data — useful during development and QA.

doUpdateVisitedHistory

Duck.ai is a single-page app. When the user navigates within it (e.g. opening a chat, switching models), the URL changes via history.pushState or history.replaceState — not a full page load. Previously, these navigations were invisible to the browser's URL bar.

This PR overrides doUpdateVisitedHistory in the WebView client to detect these SPA-style navigations and update the omnibar URL accordingly, so the address bar always reflects where the user actually is.

eTLD+1 normalization for fire button domain protection

The fire button has an allowlist of DuckDuckGo domains that are preserved when the user clears browsing data. Previously this used exact hostname matching, which missed subdomains.

The allowlist now normalizes every hostname to its eTLD+1 (public suffix + one label) at runtime before matching. This means any subdomain of a protected domain is correctly recognized, regardless of how the hostname was stored.

Steps to test this PR

Test Migration

  • filter logs by NativeStorage
  • build and install internal debug build and open app
  • go to app settings -> developer settings -> override privacy config URL and set it to https://duckduckgo.github.io/privacy-configuration/pr-4878/v4/android-config.json
  • go to app settings -> duck.ai -> URL settings and set custom url to https://euw-serp-dev-testing19.duck.ai/
  • go to app settings -> duck.ai and tap on "Native storage debug". You'll see a dialog with instructions to see the debug site
  • return to NTP and open duck.ai using the duck.ai button, or the ... overflow menu -> duck.ai
  • in the browser, open chrome://inspect -> console and create some chats that will be stored in indexedDB, using chats.generateChats({ count: 50, imagesPerChat: 1, imageSize: '1mb', target: 'indexeddb' })
  • go back to a tab with duck.ai and reload
  • Verify android log contains messages related to the migration, for instace
2026-04-08 20:44:44.498  8815-8815  chromium                com.duckduckgo.mobile.android.debug  I  [INFO:CONSOLE:423] "[NativeStorage TestUtils] Registered window.chats — available commands: generateChats, generateChat, clearAllChats, countChats, size, getStorageInfo", source: webpack-internal:///./react/src/duck-chat/utils/test/migration-test-utils.ts (423)
2026-04-08 20:44:44.916  8815-9208  DuckAiNati...ageHandler com.duckduckgo.mobile.android.debug  D  DuckAiNativeStorage: putChats count=240
2026-04-08 20:44:45.134  8815-9208  DuckAiNati...ageHandler com.duckduckgo.mobile.android.debug  D  DuckAiNativeStorage: markMigrationDone key=chats
2026-04-08 20:44:45.845  8815-9208  DuckAiNati...ageHandler com.duckduckgo.mobile.android.debug  D  DuckAiNativeStorage: putFile uuid=dab60c98-fb33-453b-811c-3c446ad7abb7 size=2796432B
2026-04-08 20:44:45.921  8815-9208  DuckAiNati...ageHandler com.duckduckgo.mobile.android.debug  D  DuckAiNativeStorage: putChat chatId=00805cf8-de47-40d3-81e5-0a330783a2af
2026-04-08 20:44:46.031  8815-9208  DuckAiNati...ageHandler com.duckduckgo.mobile.android.debug  D  DuckAiNativeStorage: putFile uuid=0b7b8a2c-7122-4678-9037-b38a07dcdf7e size=1398319B
2026-04-08 20:44:46.075  8815-9208  DuckAiNati...ageHandler com.duckduckgo.mobile.android.debug  D  DuckAiNativeStorage: putChat chatId=00955b0f-460b-48cb-916d-0ea3580e6eae
2026-04-08 20:44:46.208  8815-9208  DuckAiNati...ageHandler com.duckduckgo.mobile.android.debug  D  DuckAiNativeStorage: putFile uuid=51f07f31-4739-4c5d-921e-ab0fcb022b63 size=1398330B
...
  • Once the migration is complete, open the duck.ai side panel and very chats are in there

Test Autocomplete -- after migration is complete

  • under settings -> AI features verify the toggle is enabled
  • open NTP and tap on the address bar, switch to duck.ai
  • verify chat suggestions appear
  • click on one chat
  • verify you're taken to it

Test fire button -- after migration is complete

  • open duck.ai and select one chat
  • tap on the fire button in the toolbar -> delete chat
  • verify the chat is deleted

Test clear chat -- after migration is complete

  • open duck.ai then go to side panel
  • tap on ... in one of the chats and then "clear"
  • verify the chat is deleted
  • repeat for rename, pin and download actions

Note

Medium Risk
Introduces a new native storage layer (Room DB + on-disk files) and switches deletion/suggestions to route through a new delegation layer behind feature flags, which can affect chat history integrity and data clearing behavior. Also adds a local debug HTTP server in internal builds and new WebView history URL handling, both of which need careful verification for correctness and unintended exposure.

Overview
Adds a new duckchat-store module implementing a native Duck.ai storage bridge (Room tables for settings/chats + on-disk file storage with metadata) and a duckAiNativeStorage JS message handler to let Duck.ai read/write/migrate data outside WebView storage.

Updates Duck.ai chat suggestions and deletion to delegate between WebView-backed and native-backed implementations based on migration state and a new useNativeStorageChatData toggle, and adds new pixels for reader/deletion path selection plus migration completion.

Introduces internal-only Duck.ai developer tooling, including a new internal-feature entry and a local NanoHTTPD debug server to inspect/edit/reset native settings/chats/files and migration flags; removes the old Duck.ai URL override from the general Dev Settings screen.

Improves Duck.ai SPA navigation handling by wiring WebViewClient.doUpdateVisitedHistory into the tab/omnibar state via onHistoryUrlChanged, and hardens fire-button domain protection by normalizing preserved domains to eTLD+1 before matching.

Reviewed by Cursor Bugbot for commit 8bd82b6. Bugbot is set up for automated code reviews on this repo. Configure here.

@aitorvs aitorvs force-pushed the feature/aitorvs/duck-ai-native-storage branch from 275c12e to e13a162 Compare April 8, 2026 21:06
@aitorvs aitorvs force-pushed the feature/aitorvs/duck-ai-native-storage branch from e13a162 to ebde480 Compare April 8, 2026 21:28
Comment thread app/src/main/java/com/duckduckgo/app/browser/BrowserWebViewClient.kt Outdated
Comment thread duckchat/duck-ai-store-api/build.gradle Outdated
@aitorvs aitorvs force-pushed the feature/aitorvs/duck-ai-native-storage branch from ebde480 to 896f368 Compare April 10, 2026 11:53
Copy link
Copy Markdown
Collaborator Author

aitorvs commented Apr 10, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@aitorvs aitorvs force-pushed the feature/aitorvs/duck-ai-native-storage branch from 896f368 to 96810c2 Compare April 10, 2026 13:59
@aitorvs aitorvs force-pushed the feature/aitorvs/duck-ai-native-storage branch from 87e0d09 to eb823a7 Compare April 12, 2026 12:35
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 7 total unresolved issues (including 5 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 19cf900. Configure here.

@aitorvs aitorvs force-pushed the feature/aitorvs/duck-ai-native-storage branch from 19cf900 to 8bd82b6 Compare April 13, 2026 12:10
@aitorvs aitorvs merged commit c4c8dc3 into develop Apr 14, 2026
16 checks passed
@aitorvs aitorvs deleted the feature/aitorvs/duck-ai-native-storage branch April 14, 2026 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants