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#
| Where | How |
|---|---|
| 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 process | peek --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.app | Settings → General → Share usage and diagnostics. Turning it off also clears the helper's unsent telemetry. |
| The website | the "Share anonymous usage" switch in the page footer (stored in this browser's localStorage as peek.telemetry) |
| Every request to the peek backend | the 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(orset,unset) pushes the home's effective setting. When the file allows telemetry but the CLI's environment opts out, the CLI sendstelemetry: falseand 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,historyandlogin. - While the opt-out is forwarded, the helper records no events about that home and sends
X-Peek-Telemetry: offon 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 inconfig.jsonis never lifted this way. --no-telemetrycovers 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:
| Table | Written by |
|---|---|
peekbackend | the peek backend, directly |
peekclidaemon | the CLI and the helper (peekd), relayed through the backend |
peekfrontendanalytics | the website's automatic analytics, and Peek.app's automatic analytics (source:"mac") |
peekfrontendevents | the 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-webthrough the same-origin/api/web/telemetryroute.
The gateway#
POST /api/web/telemetry takes the body @teamofsilicons/space-station-web sends, {"table","events":[…]}, and answers 204:
- No
Idempotency-Keyis needed (browsers never send one). Space Station deduplicates events by their id, so a retried batch is harmless. - Tables: only
peekclidaemon,peekfrontendanalyticsandpeekfrontendevents. Any other table gets403 telemetry_table_unavailable;peekbackendis written by the backend alone. - Origin: a request that carries an
Originheader must come from an origin in the backend'sPEEK_WEB_ORIGINS(in production,https://peek.teamofsilicons.com, which reaches it through the same-origin/apirewrite); anything else gets403 origin_not_allowed. The CLI and the helper send noOrigin. - 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_limitedwithRetry-After). - Silent drops, still
204: when the table has no key configured on the backend, when the request carriesX-Peek-Telemetry: off, when it comes from a testing environment (X-Testing-Environment-Key), and when the backend'sPEEK_TELEMETRYis 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.hashis the first 16 hex characters ofsha256("<org>:<actor>"). Raw ids such assi:djare never recorded. contextaccepts 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:
| Source | Events |
|---|---|
| backend | http.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) |
| cli | command.finished {command, outcome, error_code, exit_code, duration_ms}, once per invocation |
| helper | daemon.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, analytics | app_launched, peek_visible {slot, mode, visible_ms}, render_error, fallback_visual {reason}, glass_mode {live or frosted}, display_changed, appearance_changed |
| Peek.app, events | shortcut_used {shortcut}, mic_pressed, keyboard_pressed, answer_submitted {ask_type, input, answer_latency_ms}, dismissed {gesture}, settings_changed {key}, simulation_run |
| website | the 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.