Skip to content

navigate_to converts bare DotNS product destinations to HTTPS before native host callbacks #533

Description

@replghost

Summary

A product calling navigateTo("calculator.paseo") reaches the native HostBridge.navigateTo callback as https://calculator.paseo.

A DotNS product identifier is not an HTTPS origin. Converting it before the platform boundary loses the distinction between Web3 product navigation and explicit HTTPS navigation, forcing native hosts to infer and reverse the conversion.

Reproduction

Using Epoca iOS with TrUAPIHostRuntime from revision 3f9ba7368f52fc3c9e8930803cb71ca46a9b1ae7:

  1. Open browse-testing.paseo as a verified product.
  2. Browse calls navigateTo("calculator.paseo") when the Calculator product is selected.
  3. Trace the native HostBridge.navigateTo(url:) callback.
  4. Observe url == "https://calculator.paseo".

The product bundle calls the TrUAPI navigation API with the bare value. The HTTPS conversion occurs inside the shared Rust runtime.

Root cause

rust/crates/truapi-server/src/host_logic/dotns.rs correctly classifies the input as NavigateDecision::DotName, preserving:

  • identifier: "calculator.paseo"
  • path: ""

It also constructs canonical_url: "https://calculator.paseo".

rust/crates/truapi-server/src/runtime.rs then collapses DotName to that canonical_url before calling the platform:

NavigateDecision::DotName { canonical_url, .. }
| NavigateDecision::Localhost { canonical_url, .. } => canonical_url,

The native callback therefore receives the HTTPS-shaped value rather than the DotNS identity.

Platform divergence

The current Android UserAgentKit ProductView path preserves the original navigation string:

  • HostRequest::NavigateTo { url } becomes NeedsNavigate { request_id, url } unchanged.
  • ProductHostAdapter invokes configuration.onNavigate(outcome.url) unchanged.

The native host-rust-core path used by iOS instead normalizes the same bare product identifier to HTTPS. An Android host adopting the same native runtime would inherit this behavior.

Impact

The native host cannot distinguish these product requests after normalization:

navigateTo("calculator.paseo")
navigateTo("https://calculator.paseo")

Both can reach the platform as https://calculator.paseo.

This prevents a host from enforcing the intended routing contract:

  • bare .paseo / dot:// destination -> verified Web3 product container
  • explicit https:// destination -> external web browser

Epoca iOS currently requires a workaround that recognizes https://*.paseo in the native callback and converts it back to a bare product destination. That restores visible navigation but cannot recover whether HTTPS was explicit in the original request.

Expected behavior

  • navigateTo("calculator.paseo") reaches the platform as a DotNS product destination without an HTTPS scheme.
  • Paths, queries, and fragments remain intact.
  • navigateTo("https://example.com") remains external HTTPS.
  • Bare DotNS and explicit HTTPS requests remain distinguishable.
  • Android and iOS native hosts receive equivalent navigation semantics.

A typed platform callback carrying NavigateDecision, or a string callback that preserves the bare DotNS identifier for DotName, would both retain the required information.

Acceptance criteria

  1. A Rust regression test asserts that navigateTo("calculator.paseo") invokes the platform callback with calculator.paseo.
  2. DotNS path, query, and fragment components are preserved.
  3. External HTTPS remains HTTPS.
  4. Bare DotNS and explicit HTTPS input remain distinguishable at the platform boundary.
  5. Native Android and iOS conformance coverage asserts the same contract.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions