|
|
Lenswire is a native HTTP(S) inspector with a local MITM proxy. Decrypt HTTPS when Lenswire CA is installed and HTTPS decryption is enabled.
- π HTTPS decryption with Lenswire CA (Generate CA β Install CA + toggle)
- π± Device VPN + local MITM: iOS (Packet Tunnel) and Android (VpnService)
- π§ Domain groups, client tags, and filters (method / status / path)
- π§ͺ Override rules: request rewrite + response mocks
- π Copy as cURL, Share HAR, fail-open bypass with clear reasons
- π§°
sandbox/probe app +sim:trust-ca/android:trust-cascripts
Primary focus: decrypt and inspect real device traffic via iOS Packet Tunnel / Android VpnService.
- Website: dmitryshelomanov.github.io/lenswire
- Repository
- Issues
- Expo SDK 57 docs
- Privacy policy:
docs/privacy.mdΒ· live page - Store assets map β what to upload where, regenerate commands
- EAS project: https://expo.dev/accounts/dshelomanovs-team/projects/lenswire
Preview APK for sideload: GitHub Releases.
- Play Console setup β AAB upload, keystore,
eas submit - Store listing copy β name, short/full description, asset paths
- Data safety answers β Play Console forms & permissions
- Listing assets:
docs/play-store/Β· phone screenshots:docs/store-screenshots/android/framed-*.png - iPhone App Store screenshots (ready):
docs/store-screenshots/*.png
npm run version:bump # bump semver + build numbers (patch by default)
npm run build:android:preview:local # APK for sideload (preferred)
npm run release:android:github # attach APK to GitHub Release
# or one-shot:
npm run release:android:preview # bump β local preview APK β GitHub Release
npm run build:android:local # AAB for Play upload (preferred)
npm run build:android:preview # cloud APK (slower)
npm run build:android # cloud AAB (slower)
npm run screenshots:store # marketing frames 1290Γ2796
npm run screenshots:play # Play graphic + Android framed screenshots| Item | Android / Play | iOS / App Store |
|---|---|---|
| App id / package in config | Done (com.lenswire.app) |
Done (com.lenswire.app + Packet Tunnel extension) |
| Icon + splash in binary | Done | Done |
| Privacy policy URL | Done (live) | Done (same URL; paste in ASC) |
| Phone listing screenshots | Done (framed-*.png via screenshots:play) |
Done (01β06.png) |
| Feature graphic / listing icon | Done (play-store/ via screenshots:play) |
n/a (uses app icon) |
| Listing copy + compliance docs | Done (LISTING, DATA-SAFETY) |
Todo (docs/app-store/ not started) |
| EAS production profile / scripts | Done (build:android:local, upload keystore on EAS) |
Todo (no build:ios / submit:ios yet) |
| Store-signed production binary | Done (AAB via local EAS β dist/android/lenswire-production-1.0.0.aab) |
Todo (.ipa via EAS) |
Real Apple Team ID in app.json |
n/a | Todo (appleTeamId is still YOUR_APPLE_TEAM_ID) |
| Network Extension entitlements | n/a | Todo (paid Apple Developer + NE capability approved) |
| Developer account + store console app | Todo (Play Console create + first AAB upload) | Todo (Apple Developer + App Store Connect) |
| Automated submit credentials | Todo (Google service account for eas submit) |
Todo (App Store Connect API key) |
| Internal test track | Todo (Play Internal testing) | Todo (TestFlight) |
| Tablet screenshots | Optional | Todo if supportsTablet stays true |
| Public production release | Todo | Todo |
Full asset map: docs/STORE-ASSETS.md. Android upload steps: docs/play-store/PLAY-CONSOLE.md.
- Node 22.13+ (see
.nvmrc) - Mac + Xcode 16+ (iOS)
- Android Studio / SDK (Android)
- For iOS device VPN: Apple Developer Program (Network Extension) + physical iPhone
- For iOS Simulator: free Personal Team is enough (tunnel capture still needs a device)
- For Android: no paid account required (
VpnService)
Shared setup once:
nvm use
npm installFirst time / after native changes (targets/network-packet-tunnel/, vendor/, entitlements, NE):
npm run prebuild:ios # expo prebuild --clean + link-hev-ios (HevSocks5Tunnel)Day-to-day:
npm run ios # Simulator (UI only β no Packet Tunnel)
npx expo run:ios --device # physical iPhone (full TUN β hev β SOCKS β MITM)| Simulator | Device | |
|---|---|---|
| App UI | yes | yes |
| Full capture (VPN) | no (IPC failed) |
yes (paid team + NE + appleTeamId) |
On device after install: Certificate β Generate/Install/Trust CA β Start β allow VPN β Safari. Details: modules/lenswire-proxy/ios/README.md.
If you ran plain expo prebuild without the npm script, link hev manually: npm run link:hev-ios.
First time / after native changes (modules/lenswire-proxy/android/, manifest, Gradle):
npm run prebuild:androidDay-to-day:
npm run android # emulator or device (full TUN β tun2socks β SOCKS β MITM)After install: Certificate β Generate CA β on rooted AVD for Chrome use npm run android:trust-ca β Start β allow VPN. Details: modules/lenswire-proxy/android/README.md.
- Certificate β Generate CA
- Install profile (device) or
npm run sim:trust-ca(Simulator) - Settings β General β About β Certificate Trust Settings β enable Lenswire CA
- Start β allow VPN (device)
- Open Safari / apps without pinning β decrypted requests appear with headers and bodies
Toggle HTTPS decryption in Settings. Apps with certificate pinning will fail while decryption is on.
Packet Tunnel does not work on Simulator (IPC failed). Full capture requires a physical device (see Run β iOS above).
After Generate CA on Simulator you can still trust it with npm run sim:trust-ca (UI / cert flows only β no tunnel capture).
npm run android
# Certificate β Generate CA
# Emulator (required for Chrome decrypt): npm run android:trust-ca
# Start β allow VPN β open https://example.comsandbox/ is a separate Expo RN app (com.lenswire.sandbox) that trusts User CAs via networkSecurityConfig. Use it to check decrypt without System CA (after Install CA), and later to verify mocks.
# Lenswire: Generate CA β Install CA β HTTPS decrypt ON
cd sandbox && npm install && npm run prebuild:android && npm run build:apk
adb install -r android/app/build/outputs/apk/release/app-release.apk
# Stop/Start VPN if you previously saw trust?/bypassed CONNECT tunnels, then GET postSee sandbox/README.md. If you only see CONNECT + trust?/bypassed, Stop VPN (clears bypass), Install CA again after Generate.
On Android 7+, Chrome and most apps ignore User CAs. Install CA alone puts the cert under Trusted credentials β User, which causes browser warnings like βcertificate is not trusted by your device's operating systemβ and pages fail to load while HTTPS decryption is ON.
Use a rooted AVD (system image without Google Play):
- Certificate β Generate CA
- From the project root:
npm run android:trust-caOn Android 14+ this overlays the Conscrypt APEX trust store (not only /system/etc/security/cacerts). Manual βInstall CAβ (User store) is not enough for Chrome. Settings β Trusted credentials may not list the CA even when trust works β rely on decrypt succeeding.
- Force-stop Chrome (script does this), then: Lenswire β HTTPS decryption Enabled β Start β open
https://example.comorhttps://m.vk.ru
Expect decrypted GET/POST rows (not only CONNECT). Google/pinned apps may stay tunnel-only.
Temporary workaround without System CA: Settings β HTTPS decryption OFF β Stop/Start (sites load again; no decrypt).
If Install CA does not open anything, install manually as User CA (not enough for Chrome):
SettingsβSecurityβEncryption & credentialsβInstall a certificateβCA certificate.
To export the generated Android CA file manually:
# Verify cert files inside app sandbox
adb shell run-as com.lenswire.app ls files/certs
# Export DER cert to current host directory
adb exec-out run-as com.lenswire.app cat files/certs/lenswire-ca.cer > lenswire-ca.cer
# Optional: copy to device Downloads
adb push lenswire-ca.cer /sdcard/Download/lenswire-ca.cerThe generated files in app sandbox are:
files/certs/lenswire-ca.cer(DER, recommended for install)files/certs/lenswire-ca.pem(PEM)
Notes:
- Android routes device traffic through TUN (
VpnService) βtun2socksβ SOCKS bridge β local MITM proxy. - iOS (device) uses the same model: Packet Tunnel
utunβ hev β SOCKS β MITM (no system HTTP proxy). Details:modules/lenswire-proxy/ios/README.md. - HTTP capture works without emulator/browser manual proxy setup.
- HTTPS Path B (TCP/443): SNI-aware MITM (SOCKS peeks ClientHello, proxy uses hostname for leaf cert).
- Fail-open: recoverable MITM failures fall back to passthrough; handshake-rejected hosts are bypassed for the session.
- QUIC / HTTP/3 is not decrypted (
quicDecrypt: false); browsers typically fall back to TCP HTTPS. SOCKS UDP ASSOCIATE is used for DNS, not for QUIC decrypt. - Tunnel-only rows show a reason (
no sni,trust?,tls off, β¦) in the traffic list and request Overview. - Pinned apps remain tunnel-only even with System CA. Lenswire cannot bypass pinning β on a rooted device unpin separately (Frida / objection / LSPosed), then decrypt again.
- Trust vs pinning: System CA fixes Chrome/browser trust; Frida-style unpinning is a separate step for apps that pin certificates.
- No Apple Developer account needed for Android (
VpnService). iOS full capture needs a paid team + Network Extension + physical device. - After regenerating the iOS native project, use
npm run prebuild:ios(runslink:hev-ios) so HevSocks5Tunnel is linked into the Packet Tunnel target.
- Paid Apple Developer team + physical iPhone
- Certificate β Generate CA β Install profile β trust in Settings
- Start β allow VPN
- Open Safari or any app β decrypted HTTPS appears in the list
VPN layer (Packet Tunnel on iOS / VpnService on Android) intercepts device traffic and forwards it to a local MITM (LocalProxyServer). HTTPS decryption works only when Lenswire CA is installed and HTTPS decryption is enabled.
iOS device: apps β PacketTunnel(utun) β hev β SOCKS β LocalProxyServer (MITM) β UI
iOS Sim: Packet Tunnel unavailable β use device for full capture
Android: VpnService(TUN) β tun2socks β SOCKS bridge β LocalProxyServer(MITM) β UI
Key components:
targets/network-packet-tunnel/: iOS Packet Tunnel (VPN interception + hev + SOCKS)modules/lenswire-proxy/: native bridge wiring the tunnel/proxy to the appLocalProxyServer: local MITM proxy (HTTPS decryption + request data to UI)modules/lenswire-proxy/android/README.md: Android capture stack (TUN β SOCKS β MITM)modules/lenswire-proxy/ios/README.md: iOS capture stack (utun β hev β SOCKS β MITM)sandbox/: separate RN probe app (checks User CA trust + mocks)app/: UI and settings (CA trust /HTTPS decryption)
| Script | Purpose |
|---|---|
npm run ios / android |
Build & run |
npm run prebuild:ios / prebuild:android |
Regenerate native projects (iOS also runs link:hev-ios) |
npm run link:hev-ios |
Link HevSocks5Tunnel into Packet Tunnel (after prebuild) |
npm run version:bump |
Bump semver + iOS/Android build numbers |
npm run build:android:preview:local |
Local EAS preview APK |
npm run release:android:preview |
Bump β local preview APK β GitHub Release |
npm run release:android:github |
Upload preview APK to GitHub Release |
npm run build:android:local |
Local EAS production AAB |
npm run screenshots:store |
Colorful marketing frames + website JPG screenshots |
npm run screenshots:play |
Play feature graphic + Android framed screenshots |
npm run website:dev / website:deploy |
Landing page (GitHub Pages) |
npm run sim:trust-ca |
Trust app-generated CA in booted iOS Simulator |
npm run android:trust-ca |
Install Lenswire CA into System store (rooted AVD) |
Built by Dmitry Shelomanov β Senior Frontend / React Native developer.

