Skip to content

Network capture, interception, and replay

CLI v3.0.2View Markdown

Public guide snapshot. View source. For exact flags in your installation, use olef commands <path> --json.

net is a host-side MITM proxy built into the single binary — no Python, no external mitmproxy. net start spawns the proxy, wires the device through adb reverse and proxy settings, and decrypted HTTP(S) transactions then stream as http events on the same timeline as screen when watch is running.

The pre-existing device proxy setting is persisted before wiring; a repeated net start repairs wiring to an already-running daemon, while net stop attempts to restore that exact setting without overwriting unowned wiring. proxy_restoration and adb_reverse_restoration are restored, not_needed, or unresolved; cleanup_complete requires both to be resolved. Recovery snapshots remain until readback verifies restoration. An unowned proxy with no daemon/snapshot is preserved with an actionable warning.

network_reachable reports only raw-IP ping plus DNS (--canary-host selects the DNS probe). application_connectivity is not_checked; verify an app HTTP request separately. The legacy connectivity_restored field is now null and must not be used as a boolean success check. Teardown errors preserve the underlying typed cause plus phase, completed_phases, and total elapsed_ms in detail. Progress is on stderr; stdout remains one terminal JSON object.

net status now exits nonzero with net_status_incomplete when a required observation fails. Its detail retains partial results, complete:false, http_proxy_state:unknown, and typed per-observation errors. Unknown match results are null. A successfully read absent proxy has http_proxy:null and http_proxy_state:known; only a complete observation returns action success.

net start --host example.com and net start --host '*.example.com' both capture the domain and its subdomains, ignoring case and matching at domain label boundaries. They do not match otherexample.com or example.com.other.example. Bare fragments such as example retain substring matching. Repeat --host for multiple scopes, or omit both --host and configured proxy.hosts to capture all hosts.

Only the leading *. wildcard form is supported. Patterns such as *example.com, api.*.com, and example.* fail before proxy startup. These rules also apply to configured proxy.hosts. The net log and intercept/rule host filters use substring matching on captured flows and do not change which hosts the proxy captures.

Each new proxy run returns a stable capture_session_id, and every captured flow records both that session and any matching rule ids. Isolate a phase with net log --session, --since 2m, --after-id, --rule-id, or create a durable boundary with net checkpoint and query it using net log --after-checkpoint. net log clear clears only queryable history while preserving an active proxy and its rules; restarting the proxy creates a new session.

Completed flows enter a bounded in-memory queue; net status exposes dropped_flows if sustained traffic outruns storage. The session JSONL keeps one 64 MiB current generation plus one rotated generation, bounding disk use to roughly 128 MiB per device session.

Beyond observing, the agent can intercept a flow — net intercept pauses matching requests/responses and emits them as http_intercept events on watch; each held event includes device-scoped net show, resume, drop, and respond actions so the agent can decide before the hold deadline. The same flow ID remains actionable across net status, net show, and the release verbs while held. Status includes its phase, held/expiry timestamps, and client connection state; a raced action reports already_released, deadline_expired, client_canceled, or unknown_id with the available lifecycle timestamps. The agent inspects with net show, then releases with net resume --set-status/--body/…, net drop, or net respond (a canned reply).

HTTP net drop <id> returns 502 by default; --set-status returns an explicit HTTP status. These drops, including fail-closed hold timeouts, persist one final flow with the synthetic response sent to the app, matched:intercept:drop, and an error. upstream_bypassed identifies request-phase drops; response-phase drops replace the captured upstream response with the synthetic result. Use net drop <id> --transport to abort the downstream connection or HTTP/2 stream without sending a response status/body. For a lost successful refresh response, first intercept the response with --at response --status 200 and inspect it before the transport abort. Capture retains the upstream 200 and body, marked intercept:transport_abort and a downstream error. The app never received that 200. This mode requires a current daemon, is HTTP-only, and cannot be combined with --set-status.

net intercept --clear disarms HTTP interception. It first checks daemon support; an older daemon returns net_http_intercept_clear_unsupported without changing its matcher. With --dir c2s or --dir s2c, it disarms the single WebSocket interception matcher instead. The other protocol’s matcher is unchanged. Clearing does not release flows or frames already held: they keep their deadlines and remain actionable with the usual release commands.

Repeated edits can be promoted to declarative net rules (map-local / map-remote / set-status / set-request-header / set-response-header / replace / block / delay). A respond rule atomically returns a synthetic status, headers, and literal or file-backed body without contacting upstream; it can match a GraphQL operationName from either the URL query or JSON POST body. Captured flows name the rule and report upstream_bypassed:true.

Header rules deliberately name their phase: use set-request-header before upstream or set-response-header before returning to the app. The ambiguous set-header kind is rejected instead of guessing.

net export fixtures writes the versioned, content-addressed bundle consumed directly by net replay --from: the loader validates its schema, paths, sizes, hashes, statuses, and headers before atomically replacing the active set. Replay keys include method, scheme, canonical host, effective port, case-sensitive path, normalized query pairs, GraphQL operation, and canonical JSON (or raw body) hash, so same-route requests cannot silently collide. Redacted, truncated, streamed, errored, or otherwise incomplete captures are refused rather than producing misleading fixtures. A corrupt or incompatible replay candidate leaves the prior active generation untouched; an older running daemon is detected by capability preflight and must be restarted before loading the bundle.

net export har|curl|fixtures hands flows to other tools by writing a durable artifact and returning an actionable summary; HAR defaults to olef-network.har, curl to olef-network.curl.sh, and fixtures to olef-fixtures unless --out selects another path.

net check <app> labels its debuggable/targetSdk result as a static heuristic and leaves the app-specific verdict unverified. With the proxy running, net check --probe <app> launches a package-scoped HTTPS canary; it reports verified/interceptable only when the app handles that intent, requests its unique URL, and the exact decrypted flow is captured.

The decrypted leg negotiates HTTP/2 or HTTP/1.1 (h2 apps aren’t downgraded), streams SSE / large bodies through instead of buffering them — both response and request (a big upload streams chunked; marked streamed/req_streamed in the flow) — decodes gzip/deflate/br/zstd for capture, rules and the app (upstream_content_encoding and upstream_resp_len in the flow keep what the server actually sent), and captures WebSocket (WS/WSS) frames. The proxy validates the real server certificate for both HTTPS and WSS, so a tampered network cannot hide behind the Olef CA; net start --insecure-upstream (or proxy.verify_upstream: false in config) accepts a self-signed dev backend you trust.

Once an in-scope connection upgrades, the proxy forwards every byte unchanged and decodes a copy of the frame stream. Inspect it hierarchically so an agent spends tokens only on the frames it needs: net ws lists sessions (id, url, per-direction message/byte counts), net ws <id> lists that session’s messages (compact dir/opcode/preview, filterable by --dir/--opcode/--grep/ --since), and net show <message-id> --body reveals a full reassembled payload (--body-file writes it binary-safe; bare net show returns metadata + preview). Fragmented messages are reassembled and permessage-deflate payloads inflated (marked compressed/decompressed, with wire_len vs payload_len). net log shows ws_open/ws_close lifecycle inline with HTTP by default; --protocol websocket|all adds per-message ws_msg events, which also stream live on watch. net ws <id> --stats summarizes a chatty socket (opcode histogram, per-direction bytes, compression ratio, rate) in one call; net show <msg> --format hex|json|protobuf decodes a payload and --frames shows a fragmented message’s per-frame breakdown; net export jsonl and net export har (with devtools _webSocketMessages) write durable dumps. Payload retention is bounded (truncated), --redact scrubs text frames, handshake headers, and close reasons, and an engine that bypasses the proxy or pins its certificate is reported (tls_error) rather than silently dropped.

Beyond observing, an agent can drive a live session in the same agent-in-the-loop model as HTTP: net inject <id> --dir s2c --text … splices a frame in (simulate a server push, or send to the server as the app; always safe, even under compression); net rule add ws-drop/ws-set-text declaratively drop or rewrite matching frames; and net intercept --dir … pauses matching frames (surfaced on watch and in net status) for net resume [--text …]/net drop. Drop/modify re-encode a frame, which is unsafe under permessage-deflate context takeover — those are forwarded unchanged and marked refused_deflate; net start --anticomp negotiates an uncompressed session where they fully apply.

By default the proxy signs with a CA it generates on first use. To reuse a CA the device already trusts — an existing mitmproxy/Charles/corporate CA — run net ca import --cert <pem> (the key can be a separate --key, or bundled in a combined PEM like mitmproxy’s mitmproxy-ca.pem); every downstream step then signs and installs your CA. net ca info shows the active CA and net ca reset returns to a generated one. Per-project CAs and the ca_trusted assertion are covered in configuration.md.

net trust (--auto, the default) installs the CA into the device’s system trust store, falling back to the user store. On a debuggable build, such as an emulator’s google_apis image, it first restarts adbd as root (adb root), which briefly drops adb forwards that Olef re-creates on the next command; --no-adb-root skips that. Physical devices and production images (including the android-tv emulator image) cannot become root: use net trust --push and finish the install in Settings, which needs a screen-lock credential. Apps that opt out of user CAs via network security config, pin their certificates, or use stacks that bypass the system trust store will reject the proxy — that failure is surfaced as a tls_error event rather than silently missing traffic.

net start --redact applies the built-in/configured policy to authorization/cookie headers, nested JSON/GraphQL body fields, JWTs, email/IP values, URL path/query values (including percent-encoded names and values), hosts, errors, and configured patterns before completed captures are persisted (the session log is written 0600 either way). Records flag redacted routing or error fields and carry policy version 2. Forwarded traffic is unchanged.

A live daemon reports its capture-policy fingerprint; requesting --redact refuses to reuse an unredacted or differently configured daemon and asks for an explicit stop/start. See security-and-redaction.md.

net only sees traffic that goes through the device proxy and trusts its CA. A tls_error usually means the app pins certificates or does not trust user CAs; clients with custom socket factories, Cronet, or QUIC bypass the proxy entirely. For a debug build, trust the Olef CA in the network security config (net ca info) or disable pinning in debug. In-process capture for such traffic is planned for the Olef runtime; the former OkHttp AAR companion has been removed.

Use an RFC 6901 JSON pointer to change one existing field, with an expected old JSON value and a limit on successful applications (default: one):

Terminal window
olef --target tv net rule add set-json --host api.example.com --path /graphql --method POST --operation-name SwitchProfile /extensions/expiresIn 600 30 1
olef --target tv net rule list

GraphQL response rules match the request’s operation name. Missing pointers, invalid JSON, or unexpected old values leave the response unchanged and record json_pointer_missing, json_body_invalid, or json_expected_value_mismatch in the rule’s runtime.last_error. runtime.rejections and applications show what actually happened; installing a rule is not proof that it applied. Limits count successful edits, including concurrent flows, and reset when the rule is reinstalled or the daemon restarts. Configured values are omitted from rule summaries. Streamed/undecodable responses are not edited. Standard flow modified and rule_ids fields identify successful mutations.

Canonical rule files use a transform action with type: "set_json", pointer, expected, value, and max_applications fields. JSON null is a value; an absent field never satisfies an expected null.

For deterministic response changes, prepare a narrowly matched rule before triggering the request, save its returned id, verify the app result, then remove that id in finally. Avoid clearing unrelated rules. Use a response-phase replacement when upstream must still execute; request-phase respond bypasses upstream. commands --guide net includes a concrete operation-scoped recipe. A configured hold deadline does not extend the app’s timeout. Client cancellation is not repaired by increasing the proxy hold; observe the outcome before replay.