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

Filters

Allow and deny lists that decide which apps and sites are recorded, applied before events reach the store.

Filters decide which apps and sites are recorded. Zanei has three capture-time scopes: all events, typed/copied bodies, and content snapshots. An unattributed clipboard change can still be stored as clipboard.copy with app.name: "Unknown" and null attribution and body because it cannot be matched to an app.

Capture-time and query-time filtering

There are two ways to narrow by app, and they do different things:

Capture-time filters (this page) Query-time filters (--app etc.)
Where it acts Before the store: matching events are discarded Only when reading data back
Purpose Keep sensitive apps out of the store Narrow results
Configured via zanei filter commands / [filter] in config.toml Per-command flags like --app, --types
From MCP Cannot be changed (read-only) Usable as query_events arguments

Managing the lists

zanei filter show                                          # all three scopes and their modes
zanei filter exclude-app add 1Password                     # all events
zanei filter text-content exclude-app add Slack             # bodies only; facts remain
zanei filter text-content only-site add github.com           # Chrome/Safari typed/copied bodies only on this site
zanei filter content-snapshot only-app add Terminal          # snapshots only from this app
zanei filter content-snapshot exclude-site add mail.google.com

The command shape is zanei filter [<scope>] <list> add|remove [VALUE]. Omit <scope> for [filter], or use text-content for [filter.text_content] and content-snapshot for [filter.content_snapshot]. The four lists have the same meaning in every scope:

List Config key suffix Meaning
exclude-app exclude_apps Exclude these apps
only-app include_only_apps When non-empty, include only these apps
exclude-site exclude_websites Exclude these browser URL hosts
only-site include_only_websites When non-empty, include only these browser URL hosts

For each app or site axis, a non-empty include_only_* list selects only mode; an empty one selects exclude mode. exclude_* always wins in either mode. The daemon reloads all three scopes at roughly 2-second intervals, so changes apply within a few seconds without a restart.

What each scope removes

The order is fixed:

  1. Built-in app exclusions.
  2. [filter] app rules, which discard every attributed event for a rejected app.
  3. [filter] website rules, which discard matching browser URL events and suppress typed/copied bodies and snapshots for that window.
  4. [filter.text_content] app and website rules. Outside this scope, input.key.text, ui.value.data.text, clipboard text/size_bytes, and element.value are null, but the event and non-content facts remain as if capture.text_content were false there.
  5. [filter.content_snapshot] app and website rules. Outside this scope, no content.snapshot event is created.

Website rules apply to Chrome and Safari, using the same scopes for standalone and embedded recording. Other browsers are controlled by app rules. Safari private-window exclusion is not guaranteed; its default content exclusions remain unchanged.

Admitting a supported browser on a filter reload starts its tracking without a recorder restart; excluding it again stops that tracking. The macOS Automation prompt appears when that browser is first admitted and the permission is first needed.

Choosing apps

zanei apps                 # installed, running, and recently recorded apps
zanei apps slack           # case-insensitive name or bundle-ID search
zanei apps slack --json    # machine-readable candidates

For every app list, add accepts a display name or bundle ID, resolves it against the same candidates, and saves the bundle ID when one exists. It prints the normalized result, such as Added com.apple.Terminal (Terminal). An unresolved value is not saved and exits with code 2, with a close candidate when available. Use --unverified only when you intentionally need to save an app that is not installed; Zanei warns that it could not verify the value.

Running add without a value opens a numbered selector ordered by recent use. In a non-TTY or with --quiet, a value is required and omission exits with code 2. remove resolves against the current list, so an uninstalled entry can still be removed. zanei filter show adds display names to resolved entries and marks unresolved hand-edited values as (not installed).

zanei apps [QUERY] [--json] needs no TCC permission and works while the daemon is stopped. The table shows name, bundle ID, installed/running/recent sources, and last use. JSON returns { "apps": [...], "recent_unavailable": null, "installed_unreadable": 0 }; if the store cannot provide recent apps, installed and running results remain and recent_unavailable explains why, while installed_unreadable counts app bundles whose metadata could not be read.

Default browser exclusions

Both content scopes exclude these browsers by default because Zanei cannot reliably identify their private windows:

Browser Bundle ID
Safari com.apple.Safari
Firefox org.mozilla.firefox
Brave com.brave.Browser
Edge com.microsoft.edgemac
Vivaldi com.vivaldi.Vivaldi
Arc company.thebrowser.Browser

Adding one of these browsers with only-app, or removing it from exclude-app, prints a warning unless --quiet is set. The defaults are editable rather than built-in blocks. In 0.3.0 they also apply to existing users with capture.text_content = true: bodies become null in these browsers by default, while events and non-content facts remain.

Matching

  • Apps — match by bundle_id (recommended; display names can change). An app that has a bundle_id is matched only by it; a display-name entry applies only to events whose app has no bundle_id. Matching is case-insensitive.
  • Sites — dot-boundary suffix match on the browser URL host: example.com also covers api.example.com, but not evil-example.com. There is no Public Suffix List handling — an entry like com matches every .com host, so prefer full domain names.
  • Precedence — if include_only_* is non-empty, an event is captured only if it is in that list and not in exclude_*. If empty, everything not excluded is captured.
  • App-level exclusion is total for attributed events — it drops all event types that can be attributed to that app (ui.*, input.*, window.*, …). An unattributed clipboard change may remain under app.name: "Unknown".

Always-on exclusions

Independent of your lists:

  • Private browsing — Chrome Incognito produces no URL events and keeps text-content-derived bodies null. Titles and interaction metadata can remain.
  • Secure Input — while Secure Input is active, Zanei takes no content snapshot.
  • Built-in exclusions — password managers and credential stores (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 default entries you see in exclude_apps in config.toml; editing those does not affect it. filter show lists the built-in entries separately from your own.

Filters and MCP

Capture-time filters cannot be modified over MCP; the MCP server is read-only. Filter management happens only through the CLI or config.toml. See MCP server.

Was this page helpful?