buffr — privacy

Two opt-in surfaces — telemetry counters and the crash reporter — are both off by default and both local-only. buffr never sends usage or crash data to a network endpoint. Not now, not ever, not even to a kryptic-owned server. The implementation is a deliberate no-op that documents the design rather than a stub waiting for an endpoint.

There is one network request buffr makes by default — see Update channel below. It can be disabled.

Update channel — one HTTP GET per day

Default-on. [updates] enabled = true in the user config. buffr makes one HTTP GET per check_interval_hours (default 24 h) against https://api.github.com/repos/kryptic-sh/buffr/releases/latest. The request carries no PII: only a User-Agent: buffr/<version> header (which GitHub mandates) and an Accept: application/vnd.github+json header. No cookies, no auth token, no telemetry payload. GitHub logs the request like any public API request (IP + timestamp); buffr does not run its own collector.

To disable entirely, set [updates] enabled = false in config.toml. That path makes zero network calls — the --check-for-updates CLI flag short-circuits without opening a socket. See Updates for the full surface.

Telemetry — opt-in usage counters

Off by default. Set [privacy] enable_telemetry = true in config.toml to opt in. When enabled, buffr writes anonymous integer counters to:

~/.local/share/buffr/usage-counters.json

(macOS: ~/Library/Application Support/sh.kryptic.buffr/usage-counters.json; Windows: %APPDATA%\kryptic\buffr\data\usage-counters.json.)

The file is pretty-printed JSON. After one app start it looks like:

{
  "app_starts": 1
}

Counters tracked:

KeyIncrements on
app_startsSuccessful CEF init.
tabs_openedEvery BrowserHost::open_tab (foreground + background).
pages_loadedEvery main-frame LoadHandler::on_load_end.
searches_runOmnibar input that falls through to the search-engine route.
bookmarks_added:bookmark cmdline (Netscape import is intentionally not).
downloads_completedDownloadHandler reports is_complete().

Counters flush every 60 s in the background plus once at clean shutdown. There is no code path that opens a network socket for telemetry — there is no endpoint to disable, no opt-out flag to flip; the network surface simply does not exist.

If you want to share counters with someone, write a script that reads the JSON and curls it to wherever you choose. buffr will not do this for you.

CLI:

buffr --telemetry-status   # print enabled/disabled, path, and current counts
buffr --reset-telemetry    # truncate counters to {}

--private mode forces telemetry off regardless of the config flag — the whole point of --private is "leave no traces".

Crash reporter — opt-in local panic capture

Off by default. Set [crash_reporter] enabled = true to opt in. When enabled, buffr installs a std::panic::set_hook that captures the panic message, panic-site location, and a Backtrace::force_capture (always on, regardless of RUST_BACKTRACE) and writes a JSON report to:

~/.local/share/buffr/crashes/<RFC3339-timestamp>.json

Filename pattern: YYYY-MM-DDTHH-MM-SS.sssZ.json (colons swapped for dashes so the path is portable to FAT/Windows).

CEF's BrowserProcessHandler does not expose an on_uncaught_exception callback in libcef-147 — the only on_uncaught_exception is on the renderer- process RenderProcessHandler and only fires for V8 exceptions (JavaScript errors). Native CEF crashes are caught by Chromium's internal crashpad/ breakpad pipeline, which buffr does not currently configure (it requires a crashpad_handler binary plus a symbol-server URL — both Phase 7 work). Phase 6 ships the panic-hook reporter only.

Reports are kept locally. Inspect them by hand:

buffr --list-crashes   # one line per report: <ts>\t<version>\t<location>\t<msg>
buffr --purge-crashes  # delete reports older than crash_reporter.purge_after_days

If you want to send a report to someone, mail the JSON file. buffr never uploads.