peekDocs
How it works / Informative

Telemetry

peek records operational telemetry by default, so failures can be traced from the CLI through the helper and the backend. It never records what anyone said, showed, asked or answered. Any one of the opt-outs below turns it off, and an explicit opt-out always wins.

Turn it off#

WhereHow
One Silicon home (CLI and its helper traffic)peek config telemetry off, or peek config set '{"telemetry":false}'. Stored in $SILICON_HOME/.peek/config.json and pushed to the helper.
One processpeek --no-telemetry …. Only that CLI process: the helper keeps following the home's config.
Environment (any of 0, false, off, no)PEEK_TELEMETRY, SPACE_STATION_TELEMETRY, SILICON_TELEMETRY. Stemcell sets SPACE_STATION_TELEMETRY=0 when its own telemetry is off, so a Silicon that opted out of Stemcell telemetry is opted out of peek's too. The CLI forwards this opt-out to the helper (below), so the helper's work for that home is not recorded either.
Peek.appSettings → General → Share usage and diagnostics. Turning it off also clears the helper's unsent telemetry.
The websitethe "Share anonymous usage" switch in the page footer (stored in this browser's localStorage as peek.telemetry)
Every request to the peek backendthe header X-Peek-Telemetry: off makes the backend skip its request events for that call. peek's CLI and helper send it for you when you opted out.
The whole backend (operators)PEEK_TELEMETRY=off disables every recorder, and the gateway accepts and drops relayed events.

The helper and environment opt-outs#

The helper (peekd) keeps working for a home after a command returns: it delivers answers, plays speech and uploads drawings. It does not share the CLI's environment, so it decides per home from that home's config.json and from what the CLI last told it with config.sync:

  • peek config telemetry off (or set, unset) pushes the home's effective setting. When the file allows telemetry but the CLI's environment opts out, the CLI sends telemetry: false and marks it as an environment opt-out.
  • Every command that talks to the helper for its home under an environment opt-out ends by forwarding it the same way. This includes send, status, register, ask, history and login.
  • While the opt-out is forwarded, the helper records no events about that home and sends X-Peek-Telemetry: off on every backend call it makes for it.
  • The opt-out stays until a command of that home runs without it and hands the helper its telemetry, or until peek config … syncs without it. An opt-out in config.json is never lifted this way.
  • --no-telemetry covers only its own process and is not forwarded.

The IAM SDK inside peek's backend has its own process-wide switch, PEEK_IAM_SDK_TELEMETRY, which follows PEEK_TELEMETRY unless an operator sets it. It records no content either.

Where it goes#

peek uses Space Station, the TOS event store, with four tables in org tos:

TableWritten by
peekbackendthe peek backend, directly
peekclidaemonthe CLI and the helper (peekd), relayed through the backend
peekfrontendanalyticsthe website's automatic analytics, and Peek.app's automatic analytics (source:"mac")
peekfrontendeventsthe website's explicit events, and Peek.app's explicit events (source:"mac")

Table keys exist only on the backend. They are never in the CLI, the helper, the app, the website bundle or a release artifact. Everything else sends events to the backend's gateway (POST /api/web/telemetry, below), which forwards them.

  • The CLI hands its event to the helper and returns immediately. With no helper (Linux, Windows, no GUI session) it posts directly with a 300 ms timeout and drops the event on failure. Telemetry never slows a command down.
  • The helper keeps at most 1,000 unsent events in its database (oldest dropped first) and sends batches of up to 40 every 10 seconds.
  • Peek.app sends its events to the helper; it has no network access of its own.
  • The website uses @teamofsilicons/space-station-web through the same-origin /api/web/telemetry route.

The gateway#

POST /api/web/telemetry takes the body @teamofsilicons/space-station-web sends, {"table","events":[…]}, and answers 204:

  • No Idempotency-Key is needed (browsers never send one). Space Station deduplicates events by their id, so a retried batch is harmless.
  • Tables: only peekclidaemon, peekfrontendanalytics and peekfrontendevents. Any other table gets 403 telemetry_table_unavailable; peekbackend is written by the backend alone.
  • Origin: a request that carries an Origin header must come from an origin in the backend's PEEK_WEB_ORIGINS (in production, https://peek.teamofsilicons.com, which reaches it through the same-origin /api rewrite); anything else gets 403 origin_not_allowed. The CLI and the helper send no Origin.
  • Size and rate: 1–40 events and at most 64 KiB per request (invalid_input, payload_too_large), and 6,000 events per minute in total (429 rate_limited with Retry-After).
  • Silent drops, still 204: when the table has no key configured on the backend, when the request carries X-Peek-Telemetry: off, when it comes from a testing environment (X-Testing-Environment-Key), and when the backend's PEEK_TELEMETRY is off. Nothing is forwarded in those cases, and the sender cannot tell, by design.

What is recorded#

Every event is self-contained:

{"schema_version":1,"app":"peek","service":"peek-backend|peek-cli|peek-daemon|peek-mac|peek-web",
 "source":"backend|cli|daemon|mac|web","version":"0.1.0","environment":"production|testing|development",
 "instance_id":"<uuidv7 per process>","trace_id":"<uuidv7 created by the CLI and carried in X-Peek-Trace-Id>",
 "step":"send.validate","event":"send.displayed","progress":1,"outcome":"ok|error|skipped|timeout",
 "duration_ms":412,"error_code":null,"isi":"deliberate",
 "actor":{"kind":"silicon","hash":"<first 16 hex of sha256('tos:si:dj')>"},
 "client":{"os":"macos","os_version":"27.0","arch":"aarch64","locale":"en_IN"},
 "context":{ /* allowlisted keys only */ }}
  • Actors are hashed. actor.hash is the first 16 hex characters of sha256("<org>:<actor>"). Raw ids such as si:dj are never recorded.
  • context accepts only these keys: slot, mode, appearance, speak_chars, show_elements, show_kinds, ask_type, options_count, drawing_bytes, drawing_sha256, answer_latency_ms, gesture, input, shortcut, ting_status, attempt, http_route (a route template, never a concrete URL), method, status, update_from, update_to, command, tts_model, tts_ttfb_ms, stt_ms, stt_language, matched, dg_request_id, key_source.

Events by source:

SourceEvents
backendhttp.completed (route template, method, status, duration; not for telemetry or health routes), auth.login, auth.refresh, auth.logout, ting.enroll, ting.send (type, status, attempt, silent), speech.token (purpose, key source, method direct or proxy), speech.proxy (relayed speech: tts_model, speak_chars, tts_ttfb_ms or stt_ms, key source, dg_request_id; never the text or audio), drawing.put, report.created, webhook.received (event type), participant.op (action, state)
clicommand.finished {command, outcome, error_code, exit_code, duration_ms}, once per invocation
helperdaemon.started, ipc.request {op, outcome, duration_ms}, send.displayed, send.queued, tts.request {tts_model, speak_chars, tts_ttfb_ms, duration_ms, status, dg_request_id}, stt.request {duration_ms, stt_ms, stt_language, matched, status}, delivery.attempt {type, status, attempt, error_code}, session.refresh {outcome}, app.update {update_from, update_to, outcome}, update.cli_watchdog
Peek.app, analyticsapp_launched, peek_visible {slot, mode, visible_ms}, render_error, fallback_visual {reason}, glass_mode {live or frosted}, display_changed, appearance_changed
Peek.app, eventsshortcut_used {shortcut}, mic_pressed, keyboard_pressed, answer_submitted {ask_type, input, answer_latency_ms}, dismissed {gesture}, settings_changed {key}, simulation_run
websitethe SDK's automatic analytics (page views, clicks with only the element tag, role and an explicit marker, errors, timings), plus install_command_copied, docs_search {results} and download_cta

What is never recorded#

  • the text of any --speak, show element, caption, question, option, answer, message or transcript;
  • audio, image bytes, image paths, drawing source;
  • SLTs, access or refresh tokens, app secrets, Deepgram keys or tokens;
  • raw Silicon or Carbon ids, and search queries typed on the website (only the number of results).

Counts and lengths (for example speak_chars, options_count) are recorded because they explain latency and failures without revealing content.

Testing contexts#

Events from a testing environment are tagged environment:"testing" and are never written to the production tables; the gateway drops them or keeps them local, bounded at 1,000. Tests of peek itself inject a fake event sink and never reach Space Station.

Verify (operators)#

With a Space Station session for org tos:

spacestation --org tos query "SELECT record.source::String s, record.event::String e, count() n FROM peekclidaemon GROUP BY s,e ORDER BY n DESC"
spacestation --org tos query "SELECT record.source::String s, record.event::String e, count() n FROM peekbackend GROUP BY s,e ORDER BY n DESC"
spacestation --org tos query "SELECT record.source::String s, record.event::String e, count() n FROM peekfrontendanalytics GROUP BY s,e ORDER BY n DESC"
spacestation --org tos query "SELECT record.source::String s, record.event::String e, count() n FROM peekfrontendevents GROUP BY s,e ORDER BY n DESC"

To confirm an opt-out, count the CLI's completion events, run a few commands with PEEK_TELEMETRY=off (for example PEEK_TELEMETRY=off peek iam --json), wait a minute, and count again. The number must not change:

spacestation --org tos query "SELECT count() n FROM peekclidaemon WHERE record.event::String = 'command.finished'"

Next#

  • Privacy: what data leaves the Mac, apart from telemetry.