Skip to content
Zanei
English
Esc
↑↓navigate↵open⌘Jpreview
On this page

Privacy model

What is recorded, what is not, and the layers that keep sensitive data out of the store.

Zanei records what you do on your computer. Because that data is sensitive, the defaults are conservative and each layer below applies without configuration.

Defaults

  1. No egress. There is no feature that sends data anywhere. Everything stays in a local SQLite file.
  2. No screen recording. No screenshots or screen-capture APIs. Zanei reads OS accessibility and event metadata.
  3. No content by default. Typing is recorded as “typing happened in this kind of field,” not what was typed. Text shown in windows is not recorded. Typed content and content snapshots are separate opt-ins.
  4. Capture-time filtering. Events attributable to excluded apps are discarded before they reach the store. An unattributed clipboard change can still be stored with app.name: "Unknown" because it cannot be matched to the excluded app. Excluded Chrome sites produce no URL events or text-content bodies.
  5. Read-only MCP exposure. The MCP server is a read-only view. An agent connected only through MCP can read the timeline but cannot change what is recorded or edit filters.
  6. Encrypted at rest. The store file is encrypted. Its key is generated on the recorder’s first start and kept in your login Keychain; it is not synced to iCloud Keychain. A copy of the file in a backup, a sync folder, or on another machine cannot be read without it, and deleting the key makes every copy unreadable.

What is recorded with the default configuration

Recorded Not recorded
Which app is frontmost, and when it changes Screenshots or screen video
Window titles and focus changes Keystroke content (only the fact of typing and the field type)
UI interactions (clicks, focus moves) as element metadata Values of password fields (AXSecureTextField), excluded at the source
The fact of typing, scrolling, and clipboard use URLs, tab info, and text-content bodies from Chrome Incognito windows
Chrome URLs and tab titles (normal windows only) Events attributable to excluded apps (password managers by default)
Text shown in app windows (content.snapshot)

Typed content is opt-in

Setting capture.text_content = true in the configuration enables recording of authorized typed characters (input.key), text inserted into fields (ui.value.data.text), and clipboard contents. For free-text fields, Zanei captures only the difference inserted within 3 seconds after a keystroke or paste in the same app and focused element; it does not capture the displayed document or the field’s full value. While you keep typing, changes are batched and recorded after a 1-second pause or at most every 5 seconds. Clipboard copy text requires a matching Command-C from the same process within 500 milliseconds. element.value stays null for free-text and unknown elements; only an allow-list of known-safe non-text controls (buttons, checkboxes, sliders) provides it. During first-time setup, if capture.text_content is not yet explicit in config.toml, an interactive background start asks you to choose once; the default is no (the exact conditions are in the CLI reference). You can change it later with zanei config set capture.text_content <true|false>.

Whenever Zanei attaches its observer to an app, it reads that application element’s role, the same read any assistive client performs. Chromium-based apps, including Electron ones, treat that read as the signal to build their accessibility tree, which is what lets macOS Accessibility classify their input fields; other native apps are unaffected. While a content opt-in is on, Zanei also tries to set AXManualAccessibility on every app the filters allow for that opt-in, not only Electron ones: Electron apps build their tree in response, and apps that do not support the attribute report it as unsupported and are unaffected. Building the tree can add a small performance cost in the affected apps.

If you opt in:

  • Password fields remain excluded; secure fields are dropped at capture time.
  • If Accessibility cannot confirm a known non-secure input field, input.key.text and pasted text remain null.
  • Value changes outside a 3-second authorization window opened by a confirmed keystroke or paste are not recorded as text. Rejected inputs do not open a window.
  • Chrome Incognito and website-filtered windows keep text-content bodies null. Window titles and interaction metadata can remain.
  • Chrome text and snapshot bodies are written only after Zanei confirms, from an observation made after the input, that the window still shows an allowed site. Text typed or shown immediately before switching to an excluded or Incognito tab is omitted.
  • Unknown UI elements do not provide element.value.
  • input.key.text is null while an input method (IME) is active and when the input-source type is unknown; committed text can be captured only as an authorized ui.value.data.text difference.
  • Voice input does not itself open an authorization window because it has no recorded keystroke or paste trigger. If voice text is inserted while a 3-second window opened by a preceding recorded keystroke or paste for the same app and focused-element generation is still active, that text can be included in the authorized ui.value.data.text difference.
  • Redactors replace recognizable matches for enabled rules; they do not make captured text anonymous.

In 0.3.0, the default [filter.text_content] scope excludes Safari, Firefox, Brave, Edge, Vivaldi, and Arc because their private windows cannot be identified reliably. This also applies to users upgrading with capture.text_content = true: typed and copied bodies in those browsers become null by default, while the events and non-content facts remain. You can change the editable defaults after reviewing the warning from zanei filter.

Content snapshots are opt-in

capture.content_snapshot = true records text that macOS Accessibility exposes in the visible part of the frontmost window. It does not take screenshots, use OCR, or call screen-recording APIs. A snapshot is considered after the window settles, periodically while you remain active in it, and when you leave it. Each event contains at most 32 KiB of UTF-8 text. data.cutoff reports time, nodes, bytes, or stopped when traversal ends early, and is null when traversal completes.

Content snapshots are independent from typed-content capture and from capture.sources. Zanei does not read secure-field subtrees or the values of single-line input fields, and it takes no snapshot while Secure Input is active. However, text you typed can still appear after it is rendered elsewhere in the window or in a multi-line text area. Snapshots can also contain messages, documents, and other text written by other people.

Choose the scope before enabling the opt-in:

zanei apps
zanei filter content-snapshot only-app add Terminal
zanei config set capture.content_snapshot true
zanei stop && zanei start

The store remains encrypted and normal retention applies. A plaintext SQLite export includes snapshot bodies unless you exclude them with --types; protect it like any other sensitive export. If you share your screen, run zanei pause first when the visible text should not be recorded. On employer-managed devices, follow your organization’s rules for third-party messages and documents.

The recorder stops creating snapshots after 128 MiB in a day. This is an in-memory backstop and restarts with the daemon. With the default 48-hour retention, it bounds snapshot text to about 256 MiB before database overhead when the daemon remains running; normal use is expected to be much lower because identical snapshots are not stored.

Automatic exclusions

  • Password fields — anything macOS marks as a secure text field is dropped at the source.
  • Chrome Incognito — Chromium reports the window mode over AppleScript. Incognito windows produce no URL events, and text-content bodies remain null. There is no configuration knob.
  • Built-in app exclusions — password managers and credential stores (for example 1Password, Keychain Access) are excluded by a hard-coded layer that cannot be lifted, not even with include_only_apps. It is separate from the editable exclude_apps defaults in config.toml.

Embedded capture policies

An application that embeds the recorder can add a [filter.capture_policy] table to its own configuration. It narrows capture further: which browser URLs may be captured, and whether IDE .env files are blocked by window title.

Which applications are recorded is not part of that table. It is decided by filter.exclude_apps and filter.include_only_apps — the same lists zanei filter manages — whether you run the recorder standalone or inside an embedding application. So zanei filter exclude-app add Slack keeps Slack out of the store in both cases, and one only-app entry switches to recording only the apps you listed.

A policy may also pin its own allowed_apps list, which is applied in addition to those two lists rather than instead of them. In every case the built-in exclusions above still cannot be lifted, and every filter, redactor, and retention rule on this page applies unchanged.

Redaction

For values that are captured, redactors replace recognizable matches for enabled rules. The defaults are credit-card numbers and tokens (redactors = ["credit_card", "token"]). Email redaction remains available and works as before when you add "email" to the filter.redactors array, but it is not enabled by default because an email address is an addressing identifier, not a secret.

Redactors do not remove names, phone or fax numbers, or postal addresses. Use capture-time filters to keep unwanted data out of the store and a short retention period to limit how long captured data remains; redaction is not the control that limits exposure. Every event carries a redaction field recording whether rules were applied.

Limitations

  • Private windows outside Chrome. Safari, Firefox, Brave, Edge, Vivaldi, and Arc do not expose a reliable private-window signal to Zanei. They are therefore excluded by default from both [filter.text_content] and [filter.content_snapshot]. These defaults are editable, but removing one can record private-window text. Their window titles are still handled by the all-events [filter] scope; to suppress those too, exclude the browser there, for example zanei filter exclude-app add com.apple.Safari.
  • Website scopes. Website rules apply to Chrome and Safari. Safari URL filtering does not imply private-window detection. Other browsers are controlled by app scope.
  • Window titles from Chrome Incognito and excluded-site windows. Their text-content bodies remain null, but Accessibility can still record the title as a window.title event. To suppress titles too, exclude Chrome: zanei filter exclude-app add com.google.Chrome.
  • Agents with shell access. An agent with shell access can use the CLI to change recording settings, filters, and stored recordings, subject to the host’s approval policy. The shipped skill instructs agents not to make these changes unless the user explicitly asks.
  • Any process that can run zanei as you. Encryption protects copies of the file, not the CLI. Anything running as your user can read the data, including an agent with shell access that can run zanei export; so can root or anything that reads process memory. Theft of the disk itself is covered by FileVault, not by Zanei.
  • After an agent reads your data. The MCP server and CLI do not send anything anywhere, but an agent that reads your timeline will typically send it to its LLM provider. That handoff is governed by the agent. The shipped skill instructs agents to narrow the time range before sending.

Intended use

Zanei records your own activity on your own machine. It is not built for monitoring others, and using it that way may be illegal in your jurisdiction. On an employer-managed device, follow your organization’s policies.

Was this page helpful?