Problem
tauri-plugin-powersync hardcodes the HTTP client it hands to the SDK:
// packages/tauri/src/lib.rs — open_database
let env = PowerSyncEnvironment::custom(
reqwest::Client::new(),
pool,
PowerSyncEnvironment::tokio_timer(),
);
PowerSyncEnvironment::custom already accepts any HttpClient, and the powersync
crate documents that as the extension point — but the Tauri plugin does not
re-expose it, and init() takes no options. So an application can configure its
own requests and not the sync stream the SDK opens internally.
Why it matters (the case that led us here)
On Android, the default reqwest client verifies TLS with
rustls-platform-verifier, and that rejects certificates which carry no OCSP
responder. Android's TrustManager mandates OCSP for revocation checking, so it
raises CertPathValidatorException: Certificate does not specify OCSP responder,
which the crate maps to Revoked:
WARN rustls_platform_verifier::verification::android] certificate was revoked:
java.security.cert.CertPathValidatorException: Certificate does not specify OCSP responder
ERROR rustls_platform_verifier::verification::android] failed to verify TLS certificate:
invalid peer certificate: Revoked
WARN powersync::sync::download::actor] Sync iteration failed, HTTP error:
error sending request for url (https://sync-…)
Modern certificates no longer ship an OCSP responder — Let's Encrypt ended OCSP
in August 2025 and Google Trust Services followed. We verified ours is genuinely
not revoked: its CRL is empty and does not contain the serial, and the edge sends
OCSP response: no response sent because there is nothing to staple. Upstream
issue: rustls/rustls-platform-verifier#221 (open).
The application can work around it for its own requests by building a client
with a bundled root store, but the sync stream stays broken — so the app looks
healthy (API reachable, UI fine) while sync silently retries forever. That is a
much harder failure to diagnose than a plain TLS error.
Suggested change (additive, backwards compatible)
Store the client on PowerSync and add an init_with_client:
pub fn init<R: Runtime>() -> TauriPlugin<R> {
init_with_client(reqwest::Client::new())
}
pub fn init_with_client<R: Runtime>(client: reqwest::Client) -> TauriPlugin<R> { … }
init() keeps its signature and behaviour. Beyond this TLS case it also lets
apps set timeouts, proxies or a user agent for the sync connection.
I have this running against 0.0.4 here, and I'm happy to open a PR against main
if you'd take it: https://github.com/jaimeshalom/powersync-js/tree/listto/injectable-http-client
Environment
tauri-plugin-powersync 0.0.4 (same hardcoding in 0.0.5 and 0.0.6)
powersync 0.0.5, reqwest 0.13, rustls-platform-verifier 0.7.0
- Android 16 (Xiaomi), release build, universal APK
Problem
tauri-plugin-powersynchardcodes the HTTP client it hands to the SDK:PowerSyncEnvironment::customalready accepts anyHttpClient, and thepowersynccrate documents that as the extension point — but the Tauri plugin does not
re-expose it, and
init()takes no options. So an application can configure itsown requests and not the sync stream the SDK opens internally.
Why it matters (the case that led us here)
On Android, the default
reqwestclient verifies TLS withrustls-platform-verifier, and that rejects certificates which carry no OCSPresponder. Android's
TrustManagermandates OCSP for revocation checking, so itraises
CertPathValidatorException: Certificate does not specify OCSP responder,which the crate maps to
Revoked:Modern certificates no longer ship an OCSP responder — Let's Encrypt ended OCSP
in August 2025 and Google Trust Services followed. We verified ours is genuinely
not revoked: its CRL is empty and does not contain the serial, and the edge sends
OCSP response: no response sentbecause there is nothing to staple. Upstreamissue: rustls/rustls-platform-verifier#221 (open).
The application can work around it for its own requests by building a client
with a bundled root store, but the sync stream stays broken — so the app looks
healthy (API reachable, UI fine) while sync silently retries forever. That is a
much harder failure to diagnose than a plain TLS error.
Suggested change (additive, backwards compatible)
Store the client on
PowerSyncand add aninit_with_client:init()keeps its signature and behaviour. Beyond this TLS case it also letsapps set timeouts, proxies or a user agent for the sync connection.
I have this running against 0.0.4 here, and I'm happy to open a PR against
mainif you'd take it: https://github.com/jaimeshalom/powersync-js/tree/listto/injectable-http-client
Environment
tauri-plugin-powersync0.0.4 (same hardcoding in 0.0.5 and 0.0.6)powersync0.0.5,reqwest0.13,rustls-platform-verifier0.7.0