Wire transparency — every outbound call, byte for byte

Published: July 19, 2026 · Updated: July 29, 2026

Outbound connections from the apps are limited to the documented OpenHeaders endpoints below, plus targets the operator configures themselves (IdP issuer for SSO, Git remotes, an audit/SIEM collector, and whatever requests the user’s own rules and workflows make). This document is the published specification of every phone-home payload; if a request is not listed here, the app does not make it. The app is fully functional with all of these unreachable or disabled — see the per-endpoint off switches.

The only usage data that ever leaves is the anonymous telemetry channel of section 5: a typed allowlist of feature-usage counts, structurally incapable of carrying URLs, headers, traffic, or identity, default-on for the desktop app, extension, and CLI with a one-switch opt-out, and hard-off for the daemon, the served web app, and the MCP server. The license system itself remains telemetry-free: license endpoints and telemetry never share identifiers, payloads, or deployments.

The browser extension’s only OpenHeaders-bound calls are that telemetry channel (section 5) and the on-demand release-notes history fetch (section 4); browser store distribution owns its updates.

1. License refresh

Self-serve subscription renewal. Node hosts only (desktop main process and the daemon — never the extension). The agent only ever delivers files: the response is verified offline against the compiled-in Ed25519 trust ring before anything persists; no online validation path exists.

{
  "licenseKey": "oh-license.<base64url payload>.<base64url signature>",
  "appVersion": "2026.7.10",
  "platform": "darwin"
}

2. Update check

Anonymous check, staging by default, and a restart that is never unprompted: an available update may download in the background (default on, one switch to off), but a running app is only ever restarted by an explicit “Update & Restart” click or a quit that happens anyway. The oh CLI additionally self-updates between invocations on self-managed binary installs (default on, oh autoupdate off to stop) — it swaps its own binary so the next run launches the new version, restarting nothing. Who checks: desktop packaged builds on macOS/Windows and Linux AppImage, the oh CLI (daily cached), and the ohd daemon only when you run ohd status or opt into its default-off unattended mode. Dev builds, deb/rpm installs, container images, and npm/brew installs make no update requests — their owning channel updates them.

3. Severity manifest

A small static severity manifest published to the update feed on each release — { latest, tag, severity, minimumSafeVersion } per app — so a security release can escalate loudly (red badge, entry banner). Severity is authored by a human before each release, never derived from anything about your install.

4. Release notes history

Each app ships its own current release notes inside the build — the What’s New screen renders them with zero network. The only wire traffic is the optional history underneath: the desktop app and the extension can list previous releases from the public changelog feed and fetch an older entry’s notes when you expand it. The CLI’s oh changelog is fully air-gapped, and the server daemon serves its own bundled notes to its admin console — the browser never dials the feed.

5. Anonymous telemetry

Anonymous usage counting — which features get used, nothing more. The event vocabulary is a typed allowlist compiled into the app: every payload property is a closed union, boolean, or number; free-form strings are banned by a guard test, so URLs, hostnames, header names or values, rule contents, request/response data, and file paths are inexpressible. The in-app inspector (Settings → General → View telemetry events) shows every event of the current session byte for byte, sent or suppressed.

{
  "schemaVersion": 2,
  "sessionId": "c0ffee00c0ffee00c0ffee00c0ffee00",
  "installId": "feedface00feedface00feedface0000",
  "sinceInstall": "2-7",
  "sentAt": 1760000000000,
  "events": [
    { "name": "first_run", "channel": "chrome-store" },
    { "name": "session_start", "host": "extension",
      "appVersion": { "year": 2026, "month": 7, "patch": 2 },
      "platform": "mac", "browser": "firefox", "locale": "en",
      "rules": "1-5", "workspaces": "0" },
    { "name": "feature_used", "feature": "workflow-editor" },
    { "name": "rule_created", "ruleType": "header" },
    { "name": "import_run", "source": "postman", "ok": true },
    { "name": "workflow_run", "ok": true },
    { "name": "error_beacon", "code": "ws-connect-failed" }
  ]
}

Everything else

Anything else leaving the process is operator-configured, not OpenHeaders-bound: the OIDC issuer you set, the Git remotes you sync, the SIEM collector you point audit streaming at, and the HTTP requests your own rules, sources, and workflows define.