# Company OS telemetry

Company OS sends nothing anywhere unless a person turns telemetry on. This
page is the complete list of what an opted-in install sends, where it goes,
how to turn it off, and what the collector does with it. If a field is not
listed here, it is not sent; adding one is a change to this file in the same
commit as the code.

## Consent

- **Off by default.** A fresh `install.sh` run on a terminal asks once
  (`Share telemetry? [y/N]`, 60-second timeout, empty answer = no). Piped or
  unattended installs never ask and stay off. The desktop app asks once on
  first launch with the same wording.
- **Recorded, never re-asked.** The choice is written to the install's `.env`
  as `HSM_TELEMETRY=1` or `0`; re-running the installer keeps it.
- **Changeable any time:** `bash install.sh --telemetry on|off`, then the
  stack restarts on the next `install.sh` run (or `docker compose up -d`).
- **No collector, no telemetry.** An installer build with no
  `HSMII_TELEMETRY_ENDPOINT` compiled in never asks and cannot enable it; the
  log says so.

Under Québec's Law 25 the vendor is the controller of what it collects; the
data below is designed to contain no personal information, and the anonymous
install id is the only identifier. Deleting `HSM_TELEMETRY_INSTALL_ID` from
`.env` (or `--telemetry off`) severs the link to earlier events.

## What is sent

Every message is one JSON envelope (`schema: hsm.telemetry.v1`) posted to
`HSM_TELEMETRY_ENDPOINT`, optionally with `Authorization: Bearer
$HSM_TELEMETRY_KEY` — a shared key that only keeps drive-by noise out of the
collector.

| Field | Content |
| --- | --- |
| `schema` | `hsm.telemetry.v1` |
| `category` | `technical_usage` for everything below |
| `event_type` | `hsm.session.start`, `hsm.dau.ping`, `hsm.heartbeat`, `llm.request` |
| `timestamp_rfc3339` | when the event was created |
| `session_id` | random UUID per process start |
| `install_id` | random UUID minted by `install.sh`, never derived from hardware, account, or network |
| `app_version` | Company OS crate version |
| `os`, `arch` | e.g. `linux`, `aarch64` (the container's, not the host's) |
| `binary` | `hsm_console` |
| `consent` | the three booleans the client was configured with; conversation and account are always `false` on installs |
| `payload` | per event, below |

`hsm.heartbeat` (every `HSM_TELEMETRY_HEARTBEAT_SECS`, default 900 s, minimum 60 s):

| Payload field | Content |
| --- | --- |
| `uptime_seconds` | seconds since the API process started |
| `interval_seconds` | the configured heartbeat interval |
| `postgres_ok` | whether `SELECT 1` succeeded |
| `workers.status` | the supervision verdict (`healthy`, `degraded`, …) |
| `workers.registered` | number of registered workers |
| `workers.failing` | number of failing workers |
| `runs_by_status` | counts of `agent_runs` started in the last interval, keyed by status (`success`, `error`, `cancelled`, `running`) |
| `companion` | the `HSM_COMPANY_OS_COMPANION` setting |
| `profile` | `companion`, `full`, or `dev` |
| `image_digest` | the first 16 hex characters of the running image digest |

`llm.request` (one per model call, from the LLM client): provider name, model
name, HTTP outcome, latency and token counts. Never the prompt or the
completion.

`hsm.session.start` and `hsm.dau.ping`: the envelope only, empty payload.

### Never sent

Prompts, completions, tool inputs or outputs, memory entries, task titles or
bodies, company names or ids, principal or user identities, hostnames, IP
addresses (the collector sees the connection's source address like any HTTP
server and does not store it), file paths, environment variables, or
credentials. The client refuses to enable conversation content on installs.

## The collector

The collector is an ordinary Company OS instance the vendor runs with
`HSM_FLEET_TELEMETRY_INGEST=1` (and optionally
`HSM_FLEET_TELEMETRY_INGEST_KEY`). Its `POST /api/company/telemetry/v1/ingest`
route:

- answers 404 on every instance that is not a collector, so a stray envelope
  never lands in a customer database;
- accepts only `hsm.telemetry.v1` envelopes with a UUID `install_id` and a
  dotted `event_type`, at most 32 KiB, at most 120 events per install per
  minute;
- stores one row per install in `fleet_installs` (first/last seen, version,
  OS, last heartbeat, event count) and the envelope in append-only
  `fleet_events`.

`GET /api/company/telemetry/v1/fleet` (operator bearer) is the read model
behind the Console's Fleet page: installs, last seen, version, last
heartbeat, 24-hour activity.

Retention is the collector operator's decision; the reconciliation lane may
delete `fleet_events` older than the retention window and never rewrites
them.

## Development and self-hosting

The same client serves operators who point `HSM_TELEMETRY_ENDPOINT` at their
own collector; `.env.example` lists every `HSM_TELEMETRY_*` variable. Set
`HSMII_TELEMETRY_ENDPOINT` (and optionally `HSMII_TELEMETRY_KEY`) when
building the distribution so the installer offers the choice.
