Self-Hosted TWA in "Soft Fail" State - Full-Screen UI Works, but Native Delegation Fails

AI-generated summary

MihirR is experiencing a “soft fail” with Digital Asset Link verification for a self-hosted Discourse site’s TWA. While the app launches full-screen, a “Running in Chrome” toast appears on every launch, and the PWA shows a browser-style notification prompt instead of the native Android dialog. This issue occurs across multiple devices and APK generators. Despite verifying the signing key, assetlinks.json, server response, Android app config, and Discourse admin settings, the problem persists. MihirR seeks insights into potential Nginx rules, firewall settings, or subtle server response issues in the default Discourse setup that might cause this partial trust scenario.

I’m trying to get native notifications working for a TWA of my self-hosted Discourse site and have run into a very strange “soft fail” with the Digital Asset Link verification.

Here are the exact symptoms:

  1. The app launches and runs full-screen without the URL bar, which suggests the asset link is partially working.

  2. However, a “Running in Chrome” toast message appears on every launch.

  3. Most importantly, when the PWA requests notification permission, it shows the browser-style prompt, not the native Android dialog. This proves that native API delegation is failing.

This behavior is consistent on all test devices (Android 12 & 13) and with APKs generated by both Bubblewrap and Microsoft’s PWABuilder.

After extensive debugging, I have confirmed that my entire client-side and public-facing server configuration appears to be perfect. The issue seems to be a subtle server-side problem that only affects the Android validator.

Here is what I have already verified:

  • Signing Key & assetlinks.json: My android.keystore SHA-256 fingerprint is a 100% exact match with the fingerprint in my live /.well-known/assetlinks.json file.

  • Server Response: My server serves the assetlinks.json URL with a 200 OK status, the correct application/json Content-Type, and no blocking CORS headers when checked with standard web tools.

  • Android App Config: The generated AndroidManifest.xml is correct and contains the standard com.google.androidbrowserhelper.trusted.DelegationService.

  • Discourse Admin Settings: My admin security settings for cors origins and Allowed crawler user agents are both empty.

Given this specific “soft fail” behavior, my question is:

Is there a known Nginx rule, firewall setting, or a subtle server response issue (like a slight delay or a non-standard header) in the default Discourse self-hosted setup that would cause the Android validator to grant a “partial trust” (allowing the full-screen UI) but deny the higher level of trust required for native API delegation?

I am at the end of what can be diagnosed from the client side. Any insight into the server’s deep configuration would be greatly appreciated.