# Security, privacy, and redaction

Source: https://olef.dev/docs/concepts/security-and-redaction

Public guide snapshot. [View source](https://github.com/olef-dev/olef/blob/4a2fd8128ec9f6be0f6e5254e1c6b81211d2170c/docs/security-and-redaction.md). For exact flags in your installation, use `olef commands <path> --json`.

Olef reads real screens, logs, files, and network traffic from a real
device. Everything below exists to keep that evidence from leaking secrets or
personal data by accident — and to be explicit where redaction *cannot* help.

## Redacting secrets and PII

Pass global `--redact` before or after any subcommand to filter JSON and text at
the shared output boundary:

```bash
olef --redact ui dump
olef log --last 2m --redact
olef --redact collect --app com.example.app --redact-screenshots
olef net start --redact
```

Built-ins cover password/secret/token/cookie fields, JWTs, bearer values,
emails, IPv4/IPv6 addresses, usernames/phone values, and common session,
device, transaction, and serial identifiers. JSON embedded in log or GraphQL
body strings is parsed and retains its structure. Each emitted record carries
the policy version and replacement count. Config can enable the policy for all
commands and add key names or Rust-regex patterns; user and project additions
are merged, with the nearest `enabled` value winning.

UI dumps also recognize values following English Birthdate/Date of Birth/DOB
and Device ID/Android ID labels. Recognized date, Android ID and UUID values
are removed from duplicate layout and summary text, and from explicitly
requested pixel redaction. This does not cover arbitrary labels, every date
locale, or text that Android does not expose; add project patterns for those.

## The network boundary

`net start --redact` applies the same policy to completed capture copies
before they reach the session log, including URL path/query values, hosts,
connection errors, WebSocket metadata, and TLS failures. 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. Request/response bytes
forwarded to the app or upstream are never changed. The session log is written
`0600` either way.

## Pixels are different

`ui screenshot` reports raw pixels as potentially sensitive. Explicit
`--redact-pixels` (PNG only) blacks out matching accessibility bounds, but
still reports that Android may not expose every rendered glyph. `collect`
marks screenshots potentially sensitive unless `--redact-screenshots`
explicitly blacks out matching bounds. The device viewer's annotation
overviews and crops are full-screen pixels too: they are captured with
`--redact-pixels` whenever the redaction policy is on (`OLEF_REDACT`
or `redaction.enabled`), and report `pixels_redacted` per screen.

Video is a stricter boundary: global `--redact` does not alter MP4 pixels.
Every `video` bundle is marked as containing sensitive, unencrypted data;
review its clips before sharing it. For video bundles, `--redact` filters
marker labels; device/session identity and recovery paths remain in the
protected manifest, so the JSON timeline is still marked potentially sensitive.
Screen recording is video-only — it does not capture device, app, or
microphone audio.

## Secrets in commands

`ui pin` enters a numeric PIN without echoing the digits back in action JSON,
and deliberately refuses `--observe`/`--expect-*` so a returned screen cannot
reveal an unmasked keypad. The PIN is still present in the local process
invocation, so use the host's normal shell-history and process-visibility
protections. See [agent-loop.md](https://olef.dev/docs/concepts/agent-loop).

App state snapshots (`app state snapshot`) are written `0700`/`0600`, record
SHA-256 hashes and a signing-identity digest, and are deliberately marked
`contains_sensitive_data:true` and unencrypted. `cleanup` overwrites before
deletion but cannot guarantee physical erasure on SSD/COW/journaled storage.
See [device-state.md](https://olef.dev/docs/concepts/device-state).

## Config cannot inject shell

Repository config cannot supply executable device-shell fragments: app
packages are validated at deserialization, permission/app-op tokens at their
command boundary, and accepted values are quoted before entering an Android
shell command. Values containing `;`, newlines, `$()`, quotes, or whitespace
fail with a typed `invalid_*` error. See
[configuration.md](https://olef.dev/docs/concepts/configuration).

## Trust boundaries of the on-device pieces

- On first `connect`, the CLI auto-installs a **version-matched APK pair**
  downloaded from the matching GitHub Release, **SHA-256 verified**, and cached
  under `~/.olef/apks/<version>/`. When working inside the Olef
  repository it prefers local Gradle build outputs.
- The wire protocol is loopback HTTP/JSON over an adb forward; the supported
  public interface for agents is the CLI surface and
  `olef commands --json`.
- The `net` MITM proxy installs its CA into the device's **user** trust store
  (`net trust`); apps that opt out of user CAs, pin certificates, or bypass the
  system store reject it, reported as `tls_error`. Forwarded bytes are never
  modified unless you explicitly intercept, rule, or replay.
- The runtime attaches only to apps the OS marks **debuggable** (it goes
  through `run-as`), never through an app dependency. Its channel is an
  abstract unix socket that accepts only adbd's uid and requires a per-attach
  token; `runtime detach` restores every hooked class.
- On the host, the runtime and debug daemons take CLI requests over a `0600`
  unix socket, or on Windows over a loopback port that answers only
  connections presenting the daemon's secret, kept in an owner-only file under
  `~/.olef`.

## The optional local usage log

Opt in to a **local usage log** with `olef usage enable`. Schema
version 2 records only the command path, duration, CLI version, outcome, and
typed error code/stage/retry posture — never argument values — and never
uploads anything:

```bash
olef usage enable
olef usage report --days 30 | jq \
  '{verbs, error_codes, error_stages, versions, recommendations, feedback_loop}'
```

Recommendations require repeated evidence: high error rates, slow p95s, or
recurring error codes. The report suggests the next engineering action but does
not edit code. Reproduce the evidence, add a regression, implement the change,
then compare error rate and p95 by version.

## Diagnostics stay passive

`why` is non-mutating: it reads the on-device server only if it is already
reachable and never installs, starts, or forwards anything to get a screen.
`collect` is passive with respect to device lifecycle: the selected device/AVD
must already be online, it only reads server-backed evidence through an
already-established session, and it never starts an AVD, server, or adb
forward. Its manifest records privacy status per artifact.

### Recovery without exposing device serials

With `--redact`, device-scoped recovery commands use local `@sd-…` handles
accepted by `--device`, or the selected project `--target`. Handle mappings
live under `~/.olef/device-refs` (0600 files in a 0700 directory on
Unix). They work only on that host; deleting a mapping invalidates its handle.
They are not credentials. Device details and application session identifiers
remain redacted, and suggested follow-ups retain `--redact`.
Internal lifecycle identities such as `avd:TV-default` are never emitted as
ADB serial arguments.
