← StreamView

Telemetry

Last updated: 31 May 2026

What StreamView sends to LTC Labs, when, and why. Short version: two anonymous events per session — app_launched and app_closed (with a session_summary blob of counts, feature flags, and config) — plus crash reports if the app blows up, plus an update check at most once a day. No personal data, no terminal content, no commands. Every category is independently opt-out-able in Settings → Data → Privacy (and Updates).

What gets sent today

Per app session — one request

StreamView posts once per session to https://www.ltclabs.dev/api/app/update-check at startup. That request carries:

  • app_launched — the new session is starting (sent immediately on init).
  • The previous session's app_closed event with its session_summary blob (below) — queued locally on shutdown and flushed at the next startup.

app_closed.session_summary

A single metadata dictionary attached to app_closed. Counts, enums, booleans only — never names, paths, or content.

Field Type What it is
session_minutes int How long the app was running
streams_opened int Streams launched this session
streams_open_at_close int Open at shutdown
workspaces_count int How many workspaces existed
most_used_layout enum Grid / MainSidebar / Rows / Single
used_tail_file bool Did you open a Tail File stream?
used_screenshot_paste bool Did the screenshot paste fire?
used_ctrl_tab bool Did the round-robin shortcut fire?
used_history_search bool Did you search history?
custom_icon_uploaded bool Any workspace has a custom uploaded image icon?
mcp_enabled bool Is MCP integration on?
custom_data_dir_set bool Is the data dir pointing somewhere other than the default?
theme enum Dark / Light / HighContrast
default_layout enum The configured default
default_shell enum PowerShell / Cmd / WSL / Git Bash
buffer_size enum bucket Scrollback setting (one of four)
idle_timeout enum bucket Idle threshold (one of four)
auto_save_sessions bool History recording on/off
startup_ms int Time from OnStartup to MainWindow shown
memory_mb_at_close int GC.GetTotalMemory(false) in MB
template_count int How many saved stream templates
pty_process_failures int Failed shell launches this session
tokens_today int Estimated tokens seen in AI-pane badges today (0 if none). No prompts or names.

Crash reports — only on a crash

Posted to /api/app/crash with the exception type, message, and stack trace. No terminal output, no file paths from your machine, no commands. Gated by Send crash reports.

Update check — at most once per 24 hours

Posted to /api/app/update-check with a random per-machine stagger. Returns the latest version + a support token. Gated by Check for updates automatically.

Support tickets — only when you press Help → Contact Support

Submitted to /api/app/support with what you typed plus the recent app log (no terminal output). You see exactly what's submitted.

What's deliberately not running

  • No heartbeat. Earlier builds pinged /api/app/telemetry every 5 minutes (~12 req/hour per running user). Disabled in 2026.5.25 — the timer is configured but never started.
  • No per-action events. workspace_added / workspace_deleted / stream_added / stream_closed / layout_changed were removed in 2026.5.25; their useful signal lives in the session_summary blob as aggregates instead.

What is never sent

Not in any payload, not in crash reports, not in update checks:

  • Terminal output or scroll-back history
  • Commands, environment variables, command-lines, working directories, file paths
  • Workspace names, stream names, template names, custom-icon filenames
  • Window contents, screenshots, keystrokes
  • Your email, account info, or anything you typed (outside a support ticket you compose)

How "anonymous" works

The machine is identified by a SHA-256 hash of Windows' MachineGuid (HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid). The raw GUID never leaves your machine. There is no user account, no login, and no personal info stored on the LTC side.

The hash gives a stable per-machine identifier so distinct machines (DAU / retention) can be counted without revealing whose machine. Different machines = different hashes; reinstalling Windows = a new hash.

Why we send anything at all

  • Is anyone using it? Counting distinct hashed machine IDs per day = daily / weekly / monthly active users.
  • Do they come back? First-seen vs last-seen of each hash = retention.
  • Does the app stay up? Crash reports + pty_process_failures surface regressions across releases.
  • Are we shipping the right defaults? most_used_layout, theme, default_shell, etc. tell us when the shipped default is wrong (e.g. if everyone changes Theme to Dark, Dark should be default).
  • Which features matter? The used_* flags tell us which features earn their keep so we know where to invest next.

Opt-out

Three independent toggles, all on by default, all in Settings:

  • Send anonymous usage data (Settings → Data → Privacy) — when OFF, suppresses app_launched, app_closed, and the session_summary blob, and drops any already-queued usage events without transmitting them. The startup /api/app/update-check request still happens (it's how the app gets its support token and license status), but its Telemetry payload is null.
  • Send crash reports (Settings → Data → Privacy) — when OFF, suppresses /api/app/crash.
  • Check for updates automatically (Settings → General → Updates) — when OFF, suppresses the periodic update check.

Turning all three off leaves only the startup /api/app/update-check request (no event payload) and the support-ticket endpoint (only fires when you submit a ticket).


Developer notes

Internal implementation reference. The canonical contract lives in ltc-website/srcDotNet/docs/TELEMETRY_CONTRACT.md; this section is the StreamView-specific wiring.

App identifier in payloads: streamview

Wire model (current)

When Endpoint
Once at startup POST /api/app/update-check — carries app_launched and any queued events (including the previous session's app_closed + session_summary)
On unhandled exception (gated) POST /api/app/crash
At most once per 24h, gated POST /api/app/update-check (update-check path)
When the user submits a support ticket POST /api/app/support

No heartbeat. Expect ~1 POST per app session per machine, plus rare crash / update / support requests.

Event vocabulary (current implementation)

Event Status
app_launched ✓ Required. Plain (no metadata).
app_closed ✓ Required. Carries the session_summary metadata blob built by LtcLabsService.EmitAppClosed.
layout_changed, stream_added, stream_closed, workspace_added, workspace_deleted — Removed in 2026.5.25 (replaced by session_summary aggregates)
probe_nested, probe_direct — Removed (internal smoke-test events)

Implementation checklist

  • Trim event vocabulary to app_launched + app_closed (2026.5.25)
  • Stop the 5-minute heartbeat (timer remains configured but never started; 2026.5.25)
  • Implement app_closed.session_summary (2026.5.25): counters in LtcLabsService.NoteXxx, snapshot built in EmitAppClosed, MainWindow's OnClosed fills in workspace/stream/custom-icon extras, App's OnStartup feeds startup_ms via a Stopwatch.
  • Settings → Data → Privacy: Send anonymous usage data + Send crash reports. Check for updates automatically lives under General → Updates. (2026.5.25)
  • On-close flush is batched — events queue to disk via SaveQueuedEvents and flush on next InitAsync (one POST).

File locations

Thing Path
Event queue (offline buffer) %APPDATA%\StreamView\telemetry_queue.json
Last update-check timestamp %LOCALAPPDATA%\StreamView\.last-update-check
Logs %LOCALAPPDATA%\StreamView\streamview.log

About-dialog disclosure

Standard paragraph from the LTC contract:

StreamView sends diagnostic and service-improvement data to LTC Labs to help us detect and fix bugs, improve stability, and understand which features are working. This data is anonymous — no personal files, content, names, or account identifiers are transmitted. You can opt out of optional usage data in Settings → Data → Privacy. Crash reports are togglable separately so you can keep stability reporting on while opting out of usage data.

Still stuck? In the app, use Help → Contact support, or open a ticket here.

An unhandled error has occurred. Reload 🗙

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please reload the page.