PRIVACY
Lustra Privacy Policy
Last updated: 2026-05-22.
Lustra is designed for customers who handle sensitive data. The product is on-premise — your reports, queries, and database credentials never leave your machine in the ordinary course of operation. This document describes the only ways anonymous data can leave your machine, both of which are opt-in.
Summary
| Surface | Default | How to enable | How to disable |
|---|---|---|---|
| Usage telemetry | Off | First-run wizard checkbox, or Settings → Privacy → Send anonymous usage data | Same toggle, any time |
| Crash reports | Off | Asked per-incident when a crash happens | Click "Skip" on the dialog |
No persistent identifier exists. There is no user ID, no machine ID, no anonymous fingerprint, no MAC address, no disk serial, no IP-based correlation. Each app launch generates a fresh random session ID that is regenerated on the next launch — two events from the same machine on different days look like two different customers.
Full event list (usage telemetry)
When you opt in, the following events may be sent in batches every 60 seconds while the app is running:
| Event | What it tells us | Properties (no PII) |
|---|---|---|
app.start / app.exit | App lifecycle | none |
report.created | A report was saved | queryCount, sheetCount |
report.opened | A report was loaded | queryCount |
report.exported | A report was exported | format (xlsx / pdf), rowCountBucket (0-1K, 1K-10K, …) |
chart.inserted | A chart was rendered | chartType (bar / line / pie / …) |
ai.query.sent | An AI request started | provider (anthropic / openai / …) |
ai.agent.completed | An AI request finished | provider, durationBucket (0-100ms, 100ms-1s, …) |
connection.created | A new database connection was added | dbEngine (postgres / mysql / mssql / …) |
schedule.created | A scheduled job was created | none |
feature.used | A feature was used | featureName |
error.encountered | A handled error fired | errorCode only — NEVER the message or stack |
Every event also carries:
appVersion— the Lustra semver currently runningosPlatform—'win32' | 'darwin' | 'linux'osRelease— major.minor only (e.g.,'10.0'for Windows 10/11). The full build number is intentionally not sent.sessionId— random UUID for the current app launchtimestamp— unix milliseconds at the moment of emit
What's never collected
Under any opt-in level, Lustra does not collect:
- File paths
- Report names, query names, field names, column names
- Query text, SQL, formula expressions
- Database connection strings, hostnames, credentials
- Customer-identifying information (email, IP address, machine ID, MAC address, disk serial)
- Any stable identifier that could be used to correlate events across app launches or machines
The technical enforcement of these rules lives in packages/core/src/telemetry/types.ts, which constrains event properties to primitive string | number | boolean values so future code changes can't accidentally smuggle a result row, a connection string, or a file path into the payload.
How to disable
- In the app: Settings → Privacy → "Send anonymous usage data" → toggle off. Any in-flight batch is dropped immediately; no new events are queued or sent.
- At install: leave the checkbox unchecked on the "Help improve Lustra" step of the first-run setup wizard. The default state is unchecked.
- Via configuration: set the environment variable
LUSTRA_TELEMETRY_DISABLED=1before launching the app. This forces network calls off regardless of the in-app toggle state (the toggle still appears in Settings; the network layer just doesn't honour it).
Crash reports
Crash reports are handled separately and are not governed by the usage-telemetry toggle. When the application crashes, you'll see a dialog with:
- The exact stack trace that would be sent (scrollable, reviewable before sending)
- A list of what's included (app version, OS, stack)
- A list of what's not included (your data, queries, file paths)
- A "Send crash report" button and a "Skip" button
The Send button is disabled for the first 2 seconds (or until you scroll the stack-trace pane, whichever comes first) so the report can't be sent before you've had a chance to look at it.
You consent per-incident. There is no global "always send crash reports" setting and we will not add one. Closing the dialog without clicking Send is equivalent to clicking Skip — no report leaves your machine.
What gets sent when you click Send:
- Application version (e.g.
0.1.0) - Operating system label (e.g.
Windows 10.0.26200) - The crash type (main-process exception or renderer-window crash)
- The stack trace shown in the dialog
What is filtered out of the stack trace before sending:
- Frames originating in
node_modules/(third-party libraries) — dropped to reduce noise - Console output and navigation history — these would otherwise attach as breadcrumbs; we disable that
What is never sent:
- Your IP address — Sentry's
sendDefaultPii: falseis set, and Sentry's ingest endpoint does not record the source IP when this flag is set - Hostname, MAC address, disk serial, or any other machine identifier
- File paths from your machine
- Database credentials or query text
- Report YAML, exported files, or any cell data
- User-agent strings
- Cached or persistent identifiers — every crash is a fresh disconnected report
Crash reports are sent to Sentry (organization bongi-io, project lustra-desktop) over HTTPS. The transport is built on @sentry/electron's main-process SDK with the Console and Breadcrumbs integrations explicitly disabled. Network failures during the send are silent — if the POST fails (no internet, Sentry outage), the report is dropped and the app exits without an error pop-up.
To disable crash reporting entirely: there is no in-app toggle, but you can set the environment variable LUSTRA_SENTRY_DISABLED=1 before launching Lustra. With that variable set, the SDK is never initialized; the crash dialog still appears (so you can see the stack trace yourself), but clicking Send is a no-op.
Data retention
Usage telemetry: 90 days, then deleted.
Crash reports: 1 year, then deleted.
These periods may be revisited as we learn how the data is actually useful, but the only direction we'll ever move them is shorter.
Server location
Telemetry events land at https://telemetry.bongi.io/ingest. Crash reports land at https://crashes.bongi.io/ingest. Both endpoints are hosted in the United States as of writing. EU residency is on the roadmap; if you have specific compliance requirements, please contact us before enabling telemetry.
Data deletion requests
Because there is no user-stable identifier, we cannot connect any event to a specific person or installation. There is therefore nothing to delete on a per-customer basis. If you've opted in and now want to revoke that consent, simply toggle telemetry off — no further data will be transmitted, and the events already sent are anonymised at the source.
For compliance enquiries: privacy@bongi.io.
Implementation references (for the technically curious)
- Consent storage: packages/core/src/telemetry/consent.ts — atomic write to
{configDir}/telemetry-consent.json, fails closed on any read/parse error. - Collector + batching: src/main/telemetry/collector.ts — 60-second batch, 1000-event queue cap, silent fail on network errors.
- Network-disabled-by-default in dev: see the
NETWORK_ENABLEDconstant in the collector — POSTs only happen whenNODE_ENV === 'production'. - DI pattern: packages/core/src/telemetry/emitter.ts — core code emits through an interface, never imports main-process types.
Future-policy changes
If we materially change what's collected, the in-app first-run wizard step records decidedAppVersion. A future build can re-prompt customers whose recorded decision predates the policy boundary. We will never silently widen what's collected; any expansion requires a fresh explicit opt-in.