diff --git a/customization/general-settings.mdx b/customization/general-settings.mdx index bc15e11..c9201b3 100644 --- a/customization/general-settings.mdx +++ b/customization/general-settings.mdx @@ -104,7 +104,7 @@ A relaunch quits the usual way: unsaved edits and a running sync still ask first ## Privacy -**Share usage data** is on. It sends one heartbeat a day with no query text, hostname, or account in it. The full payload, field by field, is on [Privacy](/security/privacy#usage-heartbeat). +Turn off **Share usage data** under **Settings > General > Privacy** to stop app usage events. It starts on. From 0.86.0, events go directly to PostHog and can link to your account. The switch does not stop account requests or server analytics. [Privacy](/security/privacy#usage-data) lists the fields and older-version behavior. ## Reset diff --git a/faq.mdx b/faq.mdx index b7a2ab0..c993d78 100644 --- a/faq.mdx +++ b/faq.mdx @@ -34,7 +34,7 @@ The GNU Affero General Public License v3.0, plugins included. A TablePro license -Your queries, results, and schemas stay on your Mac. One usage heartbeat goes out every 24 hours. It carries a hashed machine ID and facts about the app, such as its version and the types of the open connections, with no query text, hostnames, database names, or connection names; [Privacy](/security/privacy#usage-heartbeat) lists every field in it. Turn it off with **Share usage data** in **Settings > General**. AI is the exception, since the context you allow reaches the provider you configured. +Ordinary queries and results go to your database. Mac usage data is on by default and, from 0.86.0, goes directly to PostHog with installation or account identifiers. Turn off **Share usage data** in **Settings > General > Privacy**. [Privacy](/security/privacy#usage-data) lists events, fields and older-version behavior. AI providers and MCP clients receive authorized context and tool results. Publishing to Team Library or sending content to support shares that content too. diff --git a/security/privacy.mdx b/security/privacy.mdx index f01ad26..6245df3 100644 --- a/security/privacy.mdx +++ b/security/privacy.mdx @@ -1,54 +1,77 @@ --- title: Privacy -description: "Every outbound request the app can make, what each one carries, and where its off switch is" +description: "App usage fields, network endpoints and controls for Mac, Linux, iPhone and iPad" --- -Three requests leave a stock install without you asking: one usage heartbeat a day, an update check, and a fetch of the plugin catalog. None of the three carries a query, a row, a hostname, or a password. Every other channel in this table is one you switch on yourself. +Turn off app usage data in the privacy settings if you do not want to send it. Mac and Linux start with it on; the shipped iPhone and iPad app waits for your choice. This page lists technical details. The [privacy policy](https://tablepro.app/privacy) covers purposes, legal bases, recipients, retention and rights. -| Channel | Goes to | On by default | Off switch | -| --- | --- | --- | --- | -| [Usage heartbeat](#usage-heartbeat) | `api.tablepro.app` | Yes | **Settings > General**, under Privacy | -| [Update check](#update-checks) | `updates.tablepro.app` | Yes | **Settings > General**, under Software Update | -| [Plugin catalog](#the-plugin-catalog) | `updates.tablepro.app` | Yes | None | -| [AI provider](#ai-providers) | whichever provider you add | No provider is configured on a fresh install | Delete the provider, or turn off **Enable AI Features** | -| [iCloud sync](#icloud-sync) | your own iCloud account | No | **Settings > Sync** | -| [MCP clients](#the-mcp-server) | Local HTTP and a private Unix socket | No | **Settings > MCP** | -| [License and team library](#license-checks-and-the-team-library) | `api.tablepro.app` | Only after you activate a license | Do not activate one | -| [Your databases](#your-database-connections) | the hosts you type into the connection form | Only what you connect to | Do not connect | -| Crash reports | Nowhere. The app has no crash reporter. | - | - | +## Network controls -## Usage heartbeat +| Channel | Goes to | Default and control | +| --- | --- | --- | +| [Mac usage data](#usage-data), from 0.86.0 (first build 0.86.0-beta.1) | PostHog EU | On; **Settings > General > Privacy > Share usage data** | +| [Linux usage data](#linux-usage-data), from 0.1.0-beta.1 | PostHog EU | On; **Preferences > General > Privacy > Share Usage Data** | +| [iOS usage report](#iphone-and-ipad-reports) | `api.tablepro.app` | Off; **Settings > Privacy > Share Usage Data** | +| [Mac update check](#update-checks) | `updates.tablepro.app` | On; **Settings > General**, under Software Update | +| [Mac plugin catalog](#the-plugin-catalog) | `updates.tablepro.app` | On; no off switch | +| [AI provider](#ai-providers) | Your chosen provider | No provider on a fresh install; remove it or turn off **Enable AI Features** | +| [iCloud sync](#icloud-sync) | Your iCloud account | Off; **Settings > Sync** | +| [MCP clients](#the-mcp-server) | Local HTTP and a private Unix socket | Off; **Settings > MCP** | +| [Accounts and Team Library](#license-checks-and-the-team-library) | `api.tablepro.app` | Account requests after sign-in; content uploads when published | +| [Database connections](#your-database-connections) | Your configured servers | When you connect | -One `POST` to `https://api.tablepro.app/v1/analytics`, ten seconds after the app first becomes active and every 24 hours it keeps running. A 20-hour cooldown is stamped in preferences, so quitting and reopening does not send a second one. +The remaining sections describe Mac features unless a platform is named. -The JSON body holds these fields and no others: + -| Field | What it is | +## Usage data + +From Mac 0.86.0, including 0.86.0-beta.1, PostHog's SDK sends directly to `https://eu.i.posthog.com`. It also requests `/config` from `https://eu-assets.i.posthog.com` and `/flags` from the ingestion host. **Share usage data** in **Settings > General > Privacy** is on by default. The first event needs no prompt. Starting with the switch off does not initialize the SDK. A build without a PostHog project key sends nothing. + +| Event | App properties and trigger | | --- | --- | -| `machine_id` | SHA-256 of the Mac's hardware UUID, read from IOKit | -| `platform` | always `macos` | -| `app_version`, `os_version`, `architecture` | for example `0.67.0`, `macOS 15.1.0`, `arm64` | -| `locale` | the language setting: `system`, `en`, `vi`, `zh-Hans`, `zh-Hant`, `ko`, `tr`, or `fr` | -| `database_types` | the types of the connections open at that moment, such as `MySQL` | -| `connection_count` | how many connections are open | -| `has_license` | whether a license key is stored | -| `connection_attempted_at`, `connection_succeeded_at` | when you first tried to connect on this Mac, and when a connection first succeeded | -| `update_install_mode` | how updates install: `automatic`, `notify`, or `off` | -| `update_check_interval` | seconds between update checks, absent when checks are off | +| `app_opened` | Daily while running; `database_types`, `connection_count` for open connections | +| `connection_attempted` | First attempt on the installation; `database_type` | +| `connection_succeeded` | First success; `database_type` | +| `first_query_executed` | First query; `database_type` | +| `feature_gate_shown` | Once per feature per launch; `feature`, `reason` | +| `upgrade_clicked` | `source` of the click | +| `signed_in` | `method`: `interactive`, `license_key` or `migration` | +| `signed_out` | User-initiated sign-out; no app properties | +| `support_ask_shown` | License ask displayed; no app properties | +| `support_ask_answered` | `answer`: `buy`, `personal` or `not_now` | + +The SDK adds model, screen size, time zone, language, Wi-Fi state, app version/build/bundle ID, OS and SDK versions, and session ID. PostHog receives the request's IP and derives country/city. Events use a random installation ID before sign-in, without a person profile. Sign-in identifies the account and sets `app_version`, `os_version`, `architecture` and `locale` on its profile; PostHog also adds derived location. Sign-out resets the random ID. No machine ID, license status or update settings enter these events. + +No hostnames, usernames, SQL, rows or credentials enter usage data. The switch stops app events, not device registration or server events about sign-ins, purchases, devices and teams. It does not delete earlier records. See [analytics and rights](https://tablepro.app/privacy#analytics). -The **Update channel** setting is not in the body. A beta build shows only in `app_version`, which then reads like `0.82.0-beta.1`. +### Linux usage data -No query text. No hostnames, ports, database names, or usernames. No email address, no file paths, no connection names. +From 0.1.0-beta.1, turn off **Share Usage Data** in **Preferences > General > Privacy** to stop new sends. It is on by default. A request already in flight can finish. Builds without a PostHog project key send nothing. -An `X-Signature` header carries an HMAC-SHA256 of the body. It proves the payload arrived unaltered; it is not encryption, and it carries nothing about you. +The `posthog-rs` client sends to `https://eu.i.posthog.com`. It captures the events in the Mac table except `feature_gate_shown`. `signed_in.method` is `interactive`; `support_ask_answered.answer` is `buy`. The license ask's count of days used stays local. The SDK supplies library/OS metadata. Account identification sends `$identify`, `$anon_distinct_id` and `$set` with `app_version`, `os_version`, `architecture` and `locale`. Before identification, `$process_person_profile` is false. Sign-out resets the random ID. PostHog receives the IP and derives rough location. -`machine_id` hashes a hardware identifier, never anything you typed, so two heartbeats from one Mac count as one Mac. Where that identifier cannot be read, a random one is stored instead and does not survive a reset. +The installation ID and first-event flags live in `telemetry.json` under the app's state directory. No hostname, SQL, rows or credentials are event properties. Account device registration separately sends the hostname and machine ID. Turning usage data off does not stop account requests or server analytics. The [Linux policy](https://tablepro.app/privacy#linux-usage-data) states retention and rights. + +### Older Mac reports + +Before 0.86.0, the default-on switch controls a `POST` to `https://api.tablepro.app/v1/analytics`, ten seconds after activation and every 24 hours while running. A 20-hour cooldown prevents another report after reopening. + +| Field | Value | +| --- | --- | +| `machine_id` | SHA-256 of the hardware UUID; a stored random fallback if unavailable | +| `platform` | `macos` | +| `app_version`, `os_version`, `architecture`, `locale` | App/system versions, processor architecture and app language | +| `database_types`, `connection_count` | Types and count of open connections | +| `has_license` | Whether a license key is stored | +| `connection_attempted_at`, `connection_succeeded_at` | First attempt and success dates | +| `update_install_mode`, `update_check_interval` | Install mode and check interval; discarded by the current server | -The server stores each heartbeat with the IP address it came from. A [license check](#license-checks-and-the-team-library) sends the same `machine_id`, so heartbeats from a Mac with an activated license can be linked to that license. The [privacy policy](https://tablepro.app/privacy#mac-usage-report) says what the server keeps. +`X-Signature` contains an HMAC-SHA256 of the JSON. The old Mac path does not report the first query. The current server relays the report to PostHog with Cloudflare's country, without an IP address or license key, and without creating a person profile. The machine ID can link reports to account devices. The server no longer keeps a dedicated heartbeat history; processing jobs, logs, backups and the migration archive are separate. Before the November 4, 2026 server cutover, heartbeat records included IP addresses. See [retention](https://tablepro.app/privacy#retention). -**Share usage data** in **Settings > General** is on by default. With it off, the send returns before the payload is built, and nothing queues for later. +### iPhone and iPad reports -On iPhone and iPad the heartbeat is off until you choose **Share Usage Data**, and its `platform` is `ios`. See [iPhone and iPad](/ios#privacy). +The shipped app starts its heartbeat only after **Share Usage Data** is enabled at onboarding or in **Settings > Privacy**. It uses the same relay endpoint. Its fields are `machine_id` (SHA-256 of Apple's identifier for vendor), `platform` (`ios`), `app_version`, `os_version`, `architecture`, `locale`, `database_types`, `connection_count`, `has_license` (always false), `connection_attempted_at`, `connection_succeeded_at` and `first_query_executed_at`. It has no update settings and makes no account, license, update or plugin check. See [iOS privacy](https://tablepro.app/privacy#ios-app) for the separate Apple diagnostics route. ## AI providers @@ -83,12 +106,12 @@ Two provider paths run a program rather than call an endpoint. **Claude Agent** Off by default, and it needs a Pro or Team license. When it is on, records go to the CloudKit container `iCloud.com.TablePro`, into the private database of your own iCloud account, in the Production environment. -Eight record types sync: `Connection`, `ConnectionGroup`, `ConnectionTag`, `AppSettings`, `SQLFavorite`, `SQLFavoriteFolder`, `FavoriteTable`, and `SSHProfile`. A connection record carries host, port, username, database, and SSH and SSL settings. Sync runs on launch, when you switch back to the app, and about two seconds after you change something synced. +Synced record types are `Connection`, `ConnectionGroup`, `ConnectionTag`, `AppSettings`, `SQLFavorite`, `SQLFavoriteFolder`, `FavoriteTable`, `FavoriteDatabase`, `SSHProfile`, `CredentialProfile`, `TableFolder` and `TableFolderItem`. Credential profiles carry the name, username and password mode, never the password or command source. A connection record carries host, port, username, database, and SSH and SSL settings. Sync runs on launch, when you switch back to the app, and about two seconds after you change something synced. Passwords are not one of those record types. The **Passwords** toggle nested under Connections instead marks keychain items as synchronizable, so Apple's iCloud Keychain carries them end to end encrypted. It is off by default, and it applies to items written after you turn it on. -That flag is set on every keychain item the app writes, not only connection passwords: AI provider keys and the license key go through the same helper. MCP tokens and Google sign-in refresh tokens do not; their stores always write device-only items. +That flag is set on every keychain item the app writes, not only connection passwords: AI provider keys and an older license key go through the same helper. Current account tokens never sync. MCP tokens and Google sign-in refresh tokens do not; their stores always write device-only items. With password sync off, secrets are written `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`, which keeps them on that one Mac. Query history, the activity log, the execution log, tab state, MCP settings, and the sync settings themselves are never synced. Individual connections can be marked **Local only**. Full detail in [iCloud Sync](/features/icloud-sync). @@ -130,17 +153,15 @@ There is no setting that turns the catalog fetch off. ## License checks and the team library -With no license activated, the app never calls the license API. Activation sends the license key or invite code, the hashed machine ID, the Mac's name, the app version, and the OS version to `https://api.tablepro.app/v1/license`. The same fields go out on re-validation, which runs every 7 days, and on deactivation. +From Mac 0.86.0 (including 0.86.0-beta.1) and Linux 0.1.0-beta.1, sign-in uses the account service at `https://api.tablepro.app`. `POST /v2/devices` registers the machine ID, name, platform and app/OS versions. Account access tokens and signed plan files replace routine license-key checks. On Mac, the machine ID is the SHA-256 hardware identifier; on Linux it is HMAC-SHA256 of `/etc/machine-id` with `app.tablepro.TablePro`, with a stored random fallback. Refresh tokens use the Mac Keychain or Linux keyring; Mac account tokens never sync. - -The Mac's name is the only field in any request that tends to hold a person's name, because macOS builds it from the account name. It goes to the license API, never to the analytics endpoint. - +Mac builds before 0.86.0 use `https://api.tablepro.app/v1/license` after a key is entered. Activation and seven-day revalidation send the license key, machine ID, Mac name and app/OS versions. Deactivation sends the key and machine ID. Device records and audit changes are covered by the [account policy](https://tablepro.app/privacy#account). -Team Library is a Team-tier feature and it publishes only when you ask. **Share > Publish to Team Library…** uploads the connection definitions and the saved query text to `https://api.tablepro.app/v1/license/library`; the publish path builds the export envelope without credentials, so passwords, key passphrases, and TOTP secrets are not in it. A member's app pulls the library on its own weekly timer, and again straight after anyone publishes. +On Mac, publishing Team Library sends connection definitions and saved SQL to the service. Structured credentials are excluded; free text is not scrubbed. Current account builds use `/v2/teams/{id}/library`, pull changes at launch, every 15 minutes and after publishing, and publish changes rather than replacing the whole library. Older builds use `/v1/license/library` and pull at most weekly. [Team Library](https://tablepro.app/privacy#library) describes access, and [retention](https://tablepro.app/privacy#retention) distinguishes normal team deletion from deleting its sole owner's account. ## Crash reports -There are none. The app links no crash reporting SDK, its only remote packages being Sparkle, swift-certificates, and Yams, so nothing uploads a crash log, a stack trace, or a diagnostic file. Logging goes through OSLog and stays in the system log store on your Mac. +Mac 0.86.0 includes PostHog's SDK with automatic crash capture disabled. Earlier Mac builds have no crash reporter. Linux sends no automatic crash reports. Local Mac logging uses OSLog. The iPhone and iPad app has no crash SDK; Apple's separate **Share With App Developers** setting can supply crash reports and usage statistics through App Store Connect. ## Your database connections @@ -183,13 +204,13 @@ Every path here is under `~/Library/Application Support/TablePro/` unless it say | `Registry/URLCache/` | The cached plugin catalog. | | `team_library.json` | The cached team library. | | `linked_sql_index.db` | File metadata for linked SQL folders. | -| `~/Library/Preferences/com.TablePro.plist` | Settings, and the signed license blob, which carries the email on the license. | +| `~/Library/Preferences/com.TablePro.plist` | Settings; older builds also keep a signed license blob with its email. | ## Passwords and secrets Imported, synced and shared connections need [local sign-in approval](/connections/connection-form#sign-ins-from-this-mac) before using this Mac's cloud credentials or Kerberos tickets. Environment variable references have a [separate prompt](/features/connection-sharing#environment-variables). These approvals stay on the Mac that granted them. -Secrets live in the macOS keychain, under the `com.TablePro` service and in the data protection keychain: connection passwords, SSH passwords, key passphrases, SSL client key passphrases, TOTP secrets, plugin secure fields, AI provider keys, and the license key. Google sign-in refresh tokens for BigQuery and Spanner are kept in the same keychain under `com.TablePro.GoogleOAuth`, one per OAuth client ID. MCP tokens are the exception, stored as a salted SHA-256 hash; the plaintext is shown once at creation and never written down. +Secrets live in the macOS keychain, under the `com.TablePro` service and in the data protection keychain: connection passwords, SSH passwords, key passphrases, SSL client key passphrases, TOTP secrets, plugin secure fields, AI provider keys, and older license keys. Account refresh tokens use a device-only Keychain item. Google sign-in refresh tokens for BigQuery and Spanner are kept in the same keychain under `com.TablePro.GoogleOAuth`, one per OAuth client ID. MCP tokens are the exception, stored as a salted SHA-256 hash; the plaintext is shown once at creation and never written down. A connection can name a [password source](/features/connection-sharing#password-sources) instead of using the keychain: a file, an environment variable, a shell command, 1Password, HashiCorp Vault, AWS Secrets Manager, or a Vault database role. Those resolve at connect time, on your machine, and are never synced, since a path or a command belongs to one Mac. A Vault database role sends your Vault token to the address it names, and the database password it gets back stays in memory.