Security & data handling

Where your data lives, what leaves your machine, how DreamSheets is built to protect it, and how to report a vulnerability.

DreamSheets is a local-first desktop application. Your spreadsheets are ordinary files on your own computer, and the app is fully functional with no account, no network connection, and no data ever leaving your machine. Every feature that sends data anywhere is optional, off by default, and named on this page.

This page is written for the person who has to sign off on new software. If something here is unclear or you need a question answered in writing, email dc8curtis@gmail.com.

Where your data lives

SurfaceWhat it holdsWhen it's used
Your computer.dsheet files, app settings, cached credentialsAlways — this is the default and only required surface
OS credential storeAI API keys, database connection stringsOnly if you configure an AI provider or database connection
DreamSheets CloudDocument contents, file names, folder structure, sharing lists, version historyOnly if you sign in and save to the cloud
Sync relayDocument contents in transit; uploaded CSV/XLSX during conversionOnly during cloud sync, collaboration, or web upload
Your AI providerTable structure always; a five-row sample of your data by default, which you can switch offOnly if you connect your own AI key and invoke an AI feature
StripePayment detailsOnly at purchase. Card data goes directly to Stripe and never touches our systems
Usage metrics11 anonymous counters, app version, OSOnly if you opt in. Free and trial users only

Local-first by design

The app requires no account. Documents are .dsheet files — a ZIP containing JSON and Parquet — saved wherever you choose. There is no background upload, no silent sync, and no "phone home" on launch.

If your organization stores files on a network share, SharePoint, Google Drive, or OneDrive, DreamSheets documents behave like any other file there: your existing access controls, backup, and retention policies apply unchanged, and we are not involved in that storage at all.

How the application protects data

Credentials are stored in the operating system's credential manager. AI API keys, database connection strings, and your DreamSheets Cloud session tokens are held in Windows Credential Manager, macOS Keychain, or the Linux Secret Service — not in plaintext configuration files. Signing out deletes the stored tokens rather than merely forgetting the file.

Credentials are never written into .dsheet files, so sharing a document never shares a credential. A document records only the identifier of a connection; opening it on a machine without that connection yields a static snapshot, not access.

SQL in a shared document cannot touch your filesystem. Query cards carry SQL, and a document may come from someone else. The database engine used to run in-document queries is started with external filesystem access disabled and its configuration locked, so a query embedded in a file you received cannot read local files, write files, or load extensions.

Untrusted files are treated as untrusted. .dsheet loading enforces limits on entry count and decompressed size (defending against compression bombs) and validates internal identifiers before they are ever used to build a filesystem path. Malformed and hostile files produce an error message, not a crash.

Nothing executes when you open a document. Opening a file parses it and displays it. Python and R scripts run only when you explicitly run them. There is no macro auto-execution equivalent.

The user interface cannot load external code. The application window enforces a strict Content Security Policy: no external scripts, stylesheets, or fonts can be loaded, ever. All assets are bundled.

Memory safety. The application core, file format, formula engine, and all data processing are written in Rust, which eliminates buffer overflows, use-after-free, and the other memory-corruption vulnerability classes by construction.

Artificial intelligence

AI features are opt-in and bring-your-own-key: you supply credentials for a provider you have already chosen and contracted with (OpenAI, OpenRouter, a local LM Studio instance, or any compatible endpoint). We do not operate an AI service, do not proxy your requests, and never see your prompts or your key. With no key configured, no AI feature does anything.

For environments where AI must not appear at all, there is a kill switch: AI → Hide All AI Features removes every AI entry point from the interface — the AI menu, the toolbar button, the context-menu items, and the AI blocks in the query, script, and import surfaces. View → Show AI Features restores them. The setting is per installation.

What is sent, and how to change it

By default, AI assistance sends your table structure — column names, storage types, and which columns are calculated — and a five-row sample of each table, so the assistant can see the shape of real data rather than guess at it. If you connect an external AI tool through the built-in MCP server, that tool can additionally read the full document and run SQL against it.

That default is deliberate: an assistant that can see your numbers is a far more useful assistant, and for most workbooks the data is not sensitive. When it is sensitive, one setting changes the behavior — AI settings → "Don't send cell data to AI (structure only)". With it enabled:

  • AI assistance receives column names and types, and no sample rows.
  • The MCP server returns structure only from get_document, answers run_sql with the row count and the result's column names and types (never a value), and does not offer run_script at all — the tool list itself tells the agent no tool returns cell values, rather than quietly sending less.

The setting applies per installation and takes effect immediately. Choose per workbook sensitivity: leave it off when you want the assistant working directly with your numbers, switch it on before opening anything confidential.

Two guarantees that hold either way

Regardless of that setting, and not configurable:

  • The formula translator never sends cell data. When AI repairs an imported formula, the request is built to exclude cell values entirely, and the proposed formula is verified against your real data locally before anything is applied. An unverified value can never be written into your document.
  • The import analyzer sends structure, not content. When AI helps interpret a spreadsheet's layout, labels and formulas are sent, but every number, date, and boolean is replaced by a placeholder marker — because the layout question does not require the values.

Both are covered by automated tests that fail if the behavior is ever removed.

Usage metrics

Metrics are off until you turn them on, are shown to you on second launch, and stop entirely once you buy a license. When enabled, a daily summary contains: a random installation identifier (created only at the moment you opt in), the app version, your operating system, whether you are on free or trial, and counts for eleven named actions such as "documents opened" or "charts created".

The payload has no free-text field of any kind. Counter names are a compile-time allowlist, so a cell value, file name, or column name has no channel through which it could travel. Optional crash reports contain only a source-code location from our own codebase and the build version — never the error message, because an error message could quote your data.

DreamSheets Cloud (optional)

If you choose to sign in and use the cloud drive:

  • Authentication is handled by Supabase Auth — email and password, a one-time email link, or Google sign-in.
  • Access control is enforced in the database, not in the client. Every read and write passes PostgreSQL row-level security. Ownership is checked before any sharing rule, so an owner's access cannot be revoked by a malicious entry.
  • Sharing is explicit and per-recipient, by email address, as view or edit. Permissions inherit down a folder tree, and a rule on a specific document overrides the one it inherits.
  • Version history keeps prior document versions, so an unwanted change or a bad merge can be rolled back.
  • In transit, all traffic uses TLS. At rest, document blobs and database contents are encrypted by our infrastructure providers.
  • Permission changes are logged. Every share grant, revoke, ownership transfer, link-sharing change, and document deletion is written to an audit trail by the database itself, recording who did it and at what privilege level — including anything done with an administrative key rather than as a signed-in user. The trail is readable by anyone who can open the document, so it is your record, not only ours.
  • Deletion removes the document and its stored blobs.

Uploaded CSV and Excel files are converted to DreamSheets documents on our relay because that conversion requires a native engine. The uploaded file is processed and not retained as an uploaded file.

External database connections

When you connect DreamSheets to your own SQL Server, PostgreSQL, MySQL, SQLite, or DuckDB database, the desktop application connects directly from your machine to your database. Query results do not pass through our servers, and the credentials stay in your operating system's credential manager.

Transport encryption is required by default and is never silently downgraded. If a server cannot negotiate TLS, the connection fails with an error that explains the opt-out — it does not quietly continue in the clear, which is the failure mode that matters, because a silent fallback looks identical to a working encrypted connection.

  • PostgreSQL requires TLS and validates the server's certificate. If your provider signs it with its own certificate authority (Supabase does), add sslrootcert=<path to the CA file> to the connection URL: that one CA is trusted for that connection only, and nothing else about validation changes. To reach a server that does not offer TLS, add sslmode=disable. Any sslmode= you set explicitly is honoured exactly as written — we only supply the stricter default when you have not expressed a preference.
  • MySQL and MariaDB require TLS, validating both the server's certificate and its hostname. The same sslmode=disable opts out.
  • SQL Server is encrypted by default. If your connection string sets TrustServerCertificate=true, traffic is encrypted but the server's certificate is not validated — which leaves the connection open to an active attacker. Testing the connection now warns you explicitly when it sees this.
  • SQLite and DuckDB databases are local files, opened read-only. No network transport is involved.
Upgrading from an earlier release? If an existing connection stops working, the error names sslmode=disable as the opt-out. That is the deliberate escape hatch for a legacy server on a trusted network — not a bug.

Software supply chain

  • Updates are cryptographically signed. The application verifies a signature against a public key compiled into the app before applying any update. An unsigned or tampered update is refused.
  • Dependencies are audited automatically. Every change runs cargo audit and npm audit in continuous integration, as a job that fails on known vulnerabilities.
  • Releases are built from a clean, tagged commit. The release tooling refuses to build from a modified working tree.

Known limitations

We would rather you learn these here than discover them later.

  • Plugins run with the application's full privileges. A third-party plugin you install can do anything the app can do. Install only plugins you trust, from sources you trust. A permission-scoping model for plugins is planned; it does not exist today.
  • Scripts in a document are real code. Python and R cards run on your machine when you run them. Review scripts in documents you did not author, exactly as you would review a macro.
  • We are not SOC 2 audited. We are a small vendor and will tell you so plainly rather than imply a certification we do not hold. We will complete a security questionnaire on request.
  • We can technically read documents stored in DreamSheets Cloud. They are encrypted at rest by our infrastructure provider, not with a key only you hold, so an administrative key can read them. We don't, and we never use your documents for our own purposes or to train AI models, but that is a policy commitment backed by the audit trail above — not a mathematical impossibility. Any vendor without customer-managed keys or end-to-end encryption is in this position; we would rather write it down than let you assume otherwise.
  • No customer-managed encryption keys, no end-to-end encryption, and no single-sign-on for the cloud drive at this time. Documents that must be unreadable by us should stay local — the application is fully functional with no account.

Reporting a vulnerability

We welcome reports from security researchers and customers, and we will not pursue legal action against anyone who reports a genuine issue in good faith under this policy.

How to report. Email dc8curtis@gmail.com with the word "security" in the subject. Include the affected component and version, the steps to reproduce, and what an attacker could achieve. Machine-readable contact details are published at /.well-known/security.txt.

What to expect. We aim to acknowledge within 3 business days, give an initial assessment within 10 business days, and keep you updated until the issue is resolved. We will credit you when a fix ships, unless you prefer to remain anonymous.

Please do. Test only against your own installation, data, and account. Report promptly once you find something.

Please don't. Access, modify, or delete data belonging to anyone else. Run denial-of-service or automated high-volume testing against our hosted services. Use social engineering or physical intrusion. Publicly disclose before we have had a reasonable opportunity to fix the issue — 90 days is our default expectation, and we will usually be much faster.

Questions

For security questionnaires, a data processing agreement, or written answers for a procurement review, email dc8curtis@gmail.com. See also the privacy policy.