Traffic Capture & Inspection

All outbound network activity is captured as structured, queryable data. Every entry carries:

  • Method, URL, status code, latency, response size
  • Request and response headers (secret values redacted)
  • Request and response bodies (response rendered as a navigable JSON tree)
  • Scheme, host, query parameters, timestamp

The detail view has three tabs: Overview (method, status, timing), Request (headers + body), and Response (headers + body). Search across all entries, clear the list, share to Slack as formatted text or cURL, or replay the request.

Capture is automatic for OkHttp and Ktor. On iOS, you need to use the provided session configuration. For anything else, record manually.

OkHttp

Kotlin
val client = OkHttpClient.Builder()
    .addInterceptor(TrafficInterceptor())
    .build()

Ktor

Kotlin
val client = HttpClient {
    install(AlohomoraInspector)
}

iOS (URLSession)

Swift
let config = Alohomora.shared.alohomoraURLSessionConfiguration()
let session = URLSession(configuration: config)

Manual

Kotlin
Alohomora.recordTraffic(
    method = "GET",
    url = "https://api.example.com/users",
    statusCode = 200,
    requestHeaders = headers,
    responseBody = body
)

Traffic Replay

Open any captured request, edit the method, URL, headers, or body, then re-send it. The request goes through your HTTP client, so signatures are regenerated, bearer tokens are refreshed, and cert pinning works.

Register a handler once at startup:

Kotlin
// OkHttp (including Retrofit)
Alohomora.registerReplayHandler(okHttpReplayHandler(client))

// Ktor (Android + iOS)
Alohomora.registerReplayHandler(ktorReplayHandler(client))

// Custom lambda
Alohomora.registerReplayHandler { request ->
    // sign, send, return response
}

Signature headers

If your app signs requests, tell replay which headers to strip so your interceptors regenerate them:

Kotlin
ReplayHeaders.additionalStripList = setOf("X-Signature", "X-Timestamp")

Retrofit

Retrofit has no interceptors of its own — it delegates to the OkHttpClient passed to Retrofit.Builder().client(...). Register the replay handler on that same client.

Kotlin
val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(SigningInterceptor())
    .addInterceptor(TrafficInterceptor())
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(okHttpClient)
    .build()

Alohomora.registerReplayHandler(okHttpReplayHandler(okHttpClient))
Warning: Replay is a real network request with real side effects. It is hidden until a handler is registered, and hidden for requests with truncated or multipart bodies.

Mock Rules

The desktop app can intercept matching requests on the device and return canned responses before they hit the network. Each rule carries:

  • URL pattern (substring or regex)
  • Optional HTTP method filter
  • Status code and content type
  • Response body (with optional template generators)

Template generators

Use {{placeholder}} syntax in mock response bodies. Each placeholder resolves to a fresh value per request. Unknown placeholders pass through as-is.

SyntaxExample output
{{uuid}}550e8400-e29b-41d4-a716-446655440000
{{name}}Jane Smith
{{firstName}} / {{lastName}}Jane / Smith
{{email}}jane.smith@example.com
{{int(1,100)}}42
{{float(0,1)}}0.7342
{{amount(10,500)}}247.83
{{date(past,30)}}2026-07-15
{{date(future,365)}}2027-05-20
{{timestamp}}1723456789000
{{bool}}true or false
{{oneOf(active,inactive,pending)}}inactive

Persistent sessions

Rules are grouped into named sessions, saved to ~/.alohomora/mock-sessions/ as JSON. Auto-save triggers after 500 ms of inactivity. The last active session is restored on launch.

Import & export

  • Export as .alohomora-mocks.json and share with your team
  • Import from .alohomora-mocks.json or HAR 1.2 files (only 2xx responses with a body are kept)
  • One-click mock from any captured request — right-click a traffic entry to create a rule with the response pre-filled

Network Throttling

Simulate slow networks with five presets:

PresetLatencyThroughput
Edge500 ms50 KB/s
Slow 3G200 ms100 KB/s
Fast 3G100 ms300 KB/s
Slow Wi-Fi50 ms1 MB/s
None0Unlimited

Latency is applied before the response. Throughput is capped on the body.

On Android, device-wide throttling is available via a local VPN service. This covers all device traffic including WebViews, not just your HTTP client.

Database Inspection

Browse Alohomora's internal capture database and any app databases you register. Pick a database, then a table, and page through rows.

  • Run arbitrary SQL queries and see results with execution time
  • View schema: columns, types, primary keys, indexes
  • Read-only inspection (no row editing, no write history)

Registration

Kotlin
// Register a database for inspection
Alohomora.registerAppDatabase(name = "app.db")

// Exclude a database you don't want visible
Alohomora.excludeAppDatabase(name = "cache.db")

Events

Record named events with a timestamp and optional string properties. The Events panel lists entries newest-first, with search by name, expand to view properties, and clear.

Kotlin
Alohomora.recordEvent(
    name = "checkout_started",
    properties = mapOf("cart_size" to "3")
)

Cache

Read live key-value state from the device:

  • Android: SharedPreferences (including EncryptedSharedPreferences, flagged as encrypted)
  • iOS: NSUserDefaults

Keys load lazily. Click a key to fetch its current value. Read-only.

Errors

Uncaught exceptions are captured automatically. The crash handler is installed at init and chains to whatever handler was there before, so your Crashlytics/Sentry stays intact.

Manual recording

Kotlin
Alohomora.recordError(throwable, place = "SyncWorker")

Swift

Swift Error is not a KotlinThrowable, so use the dedicated overload:

Swift
Alohomora.shared.recordError(
    reason: "DecodingError: keyNotFound",
    stackTrace: Thread.callStackSymbols.joined(separator: "\n"),
    place: "ProfileLoader"
)

Features: search, full stack-trace detail view, copy to clipboard, clear.

Errors also appear in the Events timeline as App.Exception entries, rendered with an error accent bar.

iOS limitation: Covers Kotlin exceptions only. NSException and Swift fatalError require signal handlers that Alohomora deliberately does not install to avoid breaking the host app's crash reporter.

Config & Git History

Config shows build metadata: project name, variant, version name/code, branch, commit SHA, dirty flag, and build timestamp.

Git History shows the last N commits (configurable, default 50): SHA, author, subject, and timestamp.

Both are injected at build time by the Gradle plugin (Android) or an Xcode run-script phase (iOS). No runtime cost.

build.gradle.kts
alohomora {
    enabledVariants = setOf("debug")
    maxCommits = 50
    slackWebhookUrl = "https://hooks.slack.com/services/..."
    versionName = project.version.toString()
    versionCode = 1
}

Desktop-only. Accessible from the Dashboard toolbar or the command palette.

Builder tab

Compose a URL from individual parts: scheme dropdown (https, http, deeplink, content, custom), host, port, path, query parameters (add/remove rows), and fragment. A live URL preview updates as you type. Click "Open on device" to fire via adb shell am start.

History tab

Every URL you fire is persisted to ~/.alohomora/deeplink-history.json (up to 50 entries, deduplicated). Click to populate the builder, play to fire directly, trash to remove individual entries, or "Clear all" at the top.

Command Palette

Cmd/Ctrl+K opens a searchable command palette on the desktop app. Actions are grouped into four categories:

  • Navigation — one entry per sidebar section, with Cmd+1..9 shortcuts
  • General — toggle theme, help, zoom in/out/reset
  • Device — screenshot, force stop, launch, clear data, reboot, Wi-Fi/data toggle, clear logcat, deep link builder (gated on device connected)
  • Data — clear traffic, clear traces, clear events (gated on connected session)

Arrow keys to navigate, Enter to select. Each action shows its keyboard shortcut as a chip.

Desktop-Only Panels

  • Dashboard — live device metrics, deep link builder button, build info at a glance
  • Logcat — streamed device log with level filtering
  • ADB — embedded terminal (pty4j) for running arbitrary ADB commands

These panels are gated on device capability and hidden when connected to an iOS device.

Keyboard Shortcuts

ShortcutAction
Cmd/Ctrl+KOpen command palette
Cmd/Ctrl+1..9Switch to Nth sidebar section
Cmd/Ctrl+TToggle dark/light theme
Cmd/Ctrl+/Open keyboard shortcuts help
Cmd/Ctrl+NNew window
Cmd/Ctrl+WClose window
Cmd/Ctrl+=Zoom in
Cmd/Ctrl+-Zoom out
Cmd/Ctrl+0Reset zoom
Cmd/Ctrl+Shift+STake device screenshot
Cmd/Ctrl+Shift+BackspaceClear active panel data
EscapeClose side sheet / dismiss dialog