Skip to content

dev-weiqi/sniffer

Repository files navigation

Sniffer

Latest version npm Maven Central Code coverage License

Sniffer is a Kotlin Multiplatform SDK and local monitor for inspecting and mocking mobile network traffic while you develop apps.

It supports OkHttp, Ktor Client, Socket.IO, and Ktor WebSocket.

Sniffer API traffic panel

It follows the developer workflow that made Flipper useful: connect a debug app, watch traffic live, and change behavior from a desktop UI. Sniffer exists because our Flipper-based Android setup ran into the Android 16 KB page-size migration, while the upstream Flipper repository is now a public archive/read-only project. Sniffer keeps the network debugging pieces in a small stack we can maintain.

Install the monitor

The daemon runs on your dev machine: it serves the browser UI, stores mock rules, and keeps adb reverse alive for Android devices.

Prerequisites

  • Node.js 20+ (npm ships with it)
  • adb (Android platform-tools) on your PATH, for Android devices only; iOS needs nothing extra
npm install -g @dev-weiqi/sniffer
sniffer start

The UI opens in your browser at http://localhost:9091. Or skip the install and run it straight:

npx @dev-weiqi/sniffer start

If port 9091 is taken, Sniffer offers to free it for you. You can also pick another port:

PORT=9092 sniffer start

By default the daemon binds to 127.0.0.1 only, so it is not exposed to your network. Android (via adb reverse) and the iOS simulator reach it over localhost, so they are unaffected. A physical iOS/Android device connecting over Wi-Fi to your machine's LAN address needs the daemon opened up:

SNIFFER_BIND=0.0.0.0 sniffer start

SNIFFER_BIND (daemon: which interface to listen on) is the server-side twin of the client-side host you pass in the app (Sniffer.start(host = ...), which tells the device where to find the daemon). For Wi-Fi you set both: open the daemon with SNIFFER_BIND=0.0.0.0, and point the app at your machine's LAN IP.

To update an existing install to the latest version:

npm install -g @dev-weiqi/sniffer@latest

Or pin a specific version:

npm install -g @dev-weiqi/sniffer@0.4.1

Desktop app

A macOS desktop build bundles the daemon and UI into one window, with no Node.js or npm needed. Download the latest .dmg from the Releases page (Sniffer-server-mac-aarch64.dmg for Apple Silicon, Sniffer-server-mac-x64.dmg for Intel), drag Sniffer.app into /Applications, and launch. It runs the same daemon as the CLI; change port 9091 in the app's settings if needed.

"Sniffer is damaged and can't be opened" (macOS)

The dmg is not notarized by Apple yet, so Gatekeeper blocks it after download. Drag Sniffer.app into /Applications, then clear the quarantine flag once:

xattr -d com.apple.quarantine /Applications/Sniffer.app

After that it opens normally.

Add the SDK

Use core plus the transport modules your app actually uses. Add the matching -noop artifacts to release builds so production builds keep the same API with empty implementations.

Supported client integrations:

Client Sniffer artifact
OkHttp io.github.dev-weiqi.sniffer:okhttp
Ktor Client io.github.dev-weiqi.sniffer:ktor
Socket.IO io.github.dev-weiqi.sniffer:socketio
Ktor WebSocket io.github.dev-weiqi.sniffer:ktor-ws
val snifferVersion = "0.4.1"

dependencies {
    debugImplementation("io.github.dev-weiqi.sniffer:core:$snifferVersion")
    releaseImplementation("io.github.dev-weiqi.sniffer:core-noop:$snifferVersion")

    debugImplementation("io.github.dev-weiqi.sniffer:okhttp:$snifferVersion")
    releaseImplementation("io.github.dev-weiqi.sniffer:okhttp-noop:$snifferVersion")

    debugImplementation("io.github.dev-weiqi.sniffer:ktor:$snifferVersion")
    releaseImplementation("io.github.dev-weiqi.sniffer:ktor-noop:$snifferVersion")

    debugImplementation("io.github.dev-weiqi.sniffer:socketio:$snifferVersion")
    releaseImplementation("io.github.dev-weiqi.sniffer:socketio-noop:$snifferVersion")

    debugImplementation("io.github.dev-weiqi.sniffer:ktor-ws:$snifferVersion")
    releaseImplementation("io.github.dev-weiqi.sniffer:ktor-ws-noop:$snifferVersion")
}

For Kotlin Multiplatform shared code, add the KMP modules you need to commonMain:

commonMain.dependencies {
    implementation("io.github.dev-weiqi.sniffer:core:$snifferVersion")
    implementation("io.github.dev-weiqi.sniffer:ktor:$snifferVersion")
    implementation("io.github.dev-weiqi.sniffer:ktor-ws:$snifferVersion")
}

Start Sniffer

Start the SDK once when the app boots. The SDK reconnects automatically and does not throw into your app when the daemon is not running.

import dev.weiqi.sniffer.core.Sniffer

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        Sniffer.start(appId = packageName)
    }
}

Android devices and emulators reach the daemon through adb reverse, so localhost:9091 works by default. iOS simulators can also use localhost. For a physical iOS device, pass your Mac's LAN address:

Sniffer.start(appId = "com.example.app", host = "192.168.1.20")

Runtime overrides are available when you do not want to rebuild:

adb shell setprop debug.sniffer.port 9092
adb shell setprop debug.sniffer.host 192.168.1.20

Attach your clients

OkHttp:

import dev.weiqi.sniffer.okhttp.SnifferOkHttp

val okHttp = OkHttpClient.Builder()
    .addInterceptor(SnifferOkHttp.interceptor())
    .build()

Ktor client:

import dev.weiqi.sniffer.ktor.SnifferKtor

val ktor = HttpClient(CIO) {
    install(SnifferKtor)
}

Socket.IO:

import dev.weiqi.sniffer.socketio.SnifferSocketIO

val raw = IO.socket("https://api.example.com")
val socket = SnifferSocketIO.wrap(raw, "https://api.example.com")

socket.connect()
socket.emit("cart:update", mapOf("sku" to "pro"))

Apps that multiplex everything over one generic event can pass a label lambda to on: it receives the args and returns a short tag the UI shows as event(tag) in the list. Only the tag inside the parentheses is app-controlled, and the real event name is always visible. The tag is display-only: listeners, the wire event name, and mock matching all use the real name. Return null for no tag:

socket.on("message", label = { args ->
    args.optJSONObject(0)?.optString("type")?.takeIf { it.isNotEmpty() }
}) { args ->
    // normal listener
}
// server sends: message {"type":"chat",...} → listed as message(chat)

Ktor WebSocket: install the plugin once and plain webSocket calls are monitored:

import dev.weiqi.sniffer.ktorws.SnifferKtorWs

val ktor = HttpClient(CIO) {
    install(SnifferKtorWs)
    install(WebSockets)
}

val session = ktor.webSocketSession("wss://api.example.com/realtime")
session.send("ping")

Use the UI

Open http://localhost:9091, launch your debug app, and select the connected device.

  • API: inspect live requests and responses, headers, JSON, images, animated WebP responses, copied cURL commands, and mocked entries.
  • Socket: inspect Socket.IO and WebSocket connections, events, payloads, acks, and pushed server-to-client messages.
  • Mocks: create per-device HTTP response rules, delay-only rules, Socket.IO ack rules, WebSocket reply rules, and server-to-client push events.

Rules are sent to the selected device and run inside the SDK. HTTP mocks short-circuit matched requests before the network. Socket ack rules answer the client locally. Mock bodies support placeholders such as ${id} and ${randomString(length)}.

Modules

Artifact Use it for
io.github.dev-weiqi.sniffer:core SDK connection, device identity, mock rule sync
io.github.dev-weiqi.sniffer:okhttp OkHttp request/response inspection and HTTP mocks
io.github.dev-weiqi.sniffer:ktor Ktor client inspection for Android/iOS/JVM
io.github.dev-weiqi.sniffer:socketio Socket.IO event inspection, ack mocks, push events
io.github.dev-weiqi.sniffer:ktor-ws Ktor WebSocket frame inspection and reply mocks

Each artifact has a -noop twin with the same API.

Local Development

From this repository:

npm run setup
npm start

cd client
./gradlew :sample:installDebug
./gradlew :sample-cmp:installDebug

Useful checks:

cd server/daemon && npm run typecheck
cd server/ui && npm run build
cd client && ./gradlew :core:jvmTest :okhttp:test :sample:compileDebugKotlin

More detail: docs/GUIDE.md and PROTOCOL.md.

About

Self-hosted Flipper alternative: monitor and mock your app's HTTP & Socket traffic. KMP SDK + local daemon/UI.

Resources

License

Stars

3 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors