Network capture, interception, and replay
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.
Capture host scope
Section titled “Capture host scope”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.
Sessions, checkpoints, and querying
Section titled “Sessions, checkpoints, and querying”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.
Intercepting and mutating flows
Section titled “Intercepting and mutating flows”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.
Declarative rules
Section titled “Declarative rules”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.
Fixtures and replay
Section titled “Fixtures and replay”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.
Verifying an app is interceptable
Section titled “Verifying an app is interceptable”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.
Protocol coverage
Section titled “Protocol coverage”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.
WebSocket capture and drive
Section titled “WebSocket capture and drive”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.
The proxy CA
Section titled “The proxy CA”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.
Redaction at the network boundary
Section titled “Redaction at the network boundary”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.
Traffic the proxy cannot see
Section titled “Traffic the proxy cannot see”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.
Guarded JSON response edits
Section titled “Guarded JSON response edits”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):
olef --target tv net rule add set-json --host api.example.com --path /graphql --method POST --operation-name SwitchProfile /extensions/expiresIn 600 30 1olef --target tv net rule listGraphQL 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.
Prepared edits and client deadlines
Section titled “Prepared edits and client deadlines”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.