diff --git a/app/src/pages/LegalPage.test.tsx b/app/src/pages/LegalPage.test.tsx index 5be53ca58c1..dd3141a569d 100644 --- a/app/src/pages/LegalPage.test.tsx +++ b/app/src/pages/LegalPage.test.tsx @@ -93,6 +93,47 @@ describe('LegalPage', () => { expect(screen.getByText(/Cloudflare stands in front of the site/)).toBeInTheDocument(); }); + // The data assistant's section, each claim pinned with the qualifier that + // makes it true: a preview only admins reach, training excluded only under + // Vertex AI's terms, telemetry pseudonymous (not anonymous), the data file + // ticked by default, and a case row that has no timer while its files do. + it('describes the data assistant preview without overclaiming', () => { + render(); + + expect( + screen.getByText(/only the operator and the\s+site.s admins can open/) + ).toBeInTheDocument(); + expect( + screen.getByText(/served through Google Cloud Vertex AI in the\s+EU/) + ).toBeInTheDocument(); + expect( + screen.getByText(/processed under Google Cloud.s data\s+processing terms/) + ).toBeInTheDocument(); + expect( + screen.getByText(/don.t paste personal data you are not allowed to\s+share/) + ).toBeInTheDocument(); + expect(screen.getByText(/there is no session database/)).toBeInTheDocument(); + expect(screen.getByText(/pseudonymous, not\s+anonymous/)).toBeInTheDocument(); + expect(screen.getByText(/up to 180 days/)).toBeInTheDocument(); + expect(screen.getByText(/keyed hash \(HMAC\)/)).toBeInTheDocument(); + // Unticking the box drops data.csv only; the profile sample stays. + expect(screen.getByText(/unticking leaves out that file only/)).toBeInTheDocument(); + expect(screen.getByText(/first 5 rows as a sample/)).toBeInTheDocument(); + // The renderer's answer cache has no idle timer of its own. + expect(screen.getByText(/keeps its last 16 results/)).toBeInTheDocument(); + expect(screen.getByText(/nothing deletes it on a timer/)).toBeInTheDocument(); + expect(screen.getByText(/Art\. 6\(1\)\(b\) GDPR/)).toBeInTheDocument(); + expect(screen.getByText(/Art\. 6\(1\)\(f\) GDPR/)).toBeInTheDocument(); + // Scope: the assistant's processing touches only the people who use it. + expect(screen.getByText('only if you use the data assistant (preview)')).toBeInTheDocument(); + expect(screen.getByText(/if you\s+only browse the catalogue, none of it/)).toBeInTheDocument(); + expect( + screen.getByText(/Claude models by Anthropic \(EU\) — only when you use the\s+data assistant/) + ).toBeInTheDocument(); + // Browsing the catalogue still involves no model; only the assistant does. + expect(screen.getByText(/never sees a visitor.s request/)).toBeInTheDocument(); + }); + it('renders the technology stack', () => { render(); diff --git a/app/src/pages/LegalPage.tsx b/app/src/pages/LegalPage.tsx index 0aba7534dcf..4db795b06ba 100644 --- a/app/src/pages/LegalPage.tsx +++ b/app/src/pages/LegalPage.tsx @@ -189,9 +189,10 @@ export function LegalPage() { data controller - Markus Neusinger (see legal notice above). the ground for all of it is an + Markus Neusinger (see legal notice above). the ground for the site itself is an operator's legitimate interest in protecting the site and knowing how often it is - read; swiss data protection law applies, and the GDPR on top for visitors from the EU. + read; the data assistant below, which only its users meet, names its own grounds. + swiss data protection law applies, and the GDPR on top for visitors from the EU. what we collect @@ -247,6 +248,65 @@ export function LegalPage() { front of a request — and to absorb DDoS attacks. + + only if you use the data assistant (preview) + + + everything in this part applies only to people who use the data assistant. if you only + browse the catalogue, none of it touches you. anyplot is building an assistant that + adapts a catalogue plot to data you bring ("use with my data"). today it is + a preview that only the operator and the site's admins can open; nobody else can + reach it yet. this is what it does with what you give it. + + + what the model sees. what you paste or drop and the messages you + write go to Claude models by Anthropic, served through Google Cloud Vertex AI in the + EU, to make the plot you asked for. they are processed under Google Cloud's data + processing terms, and under Google Cloud's terms for Vertex AI your prompts are + not used to train the models. don't paste personal data you are not allowed to + share. + + + sessions. a session — your data, the conversation and the plots made + from it — is held in the server's memory only. it is cleared after an hour + without activity or when the server instance stops; there is no session database. the + separate render service also keeps its last 16 results (converted data, plot images) + in memory, until newer renders push them out or its instance stops. + + + usage telemetry. to see how the assistant is used and where it fails, + it writes content-free lines: event types, data formats, size buckets, the plot type + and library, attempts, review defect ids, statuses, latencies and token counts. a line + never holds your text, your data, your column names or your file names. the lines of + one session share a salted hash of the session, whose salt lives only as long as the + server instance, and they carry no user id — pseudonymous, not anonymous. they are + kept for up to 180 days in a Google Cloud Logging bucket in the Netherlands + (europe-west4) and read through BigQuery. + + + attribution and rate limits. the assistant's own log lines, + which debug it and enforce the per-user limits, carry a keyed hash (HMAC) of your + identity in place of it, and no content. they sit in the server logs above, for 30 + days. + + + feedback cases. only when you send feedback on a result or on the + chat does the assistant store a case: the conversation, the profile of your data + (column types and the first 5 rows as a sample), the plot's code and images if + one was made and, while the "# include data.csv" box is ticked — it is by + default — the whole data file. unticking leaves out that file only: what the + conversation, the sample, the code and the images show of your data stays in the case. + the files go to a private bucket in the Netherlands (europe-west4) and are deleted + after 180 days. a row with your reaction, your short message, a keyed hash of your + identity and the technical details of the run (plot type, library, model) stays for + the triage, and nothing deletes it on a timer — ask and yours is gone. + + + legal basis. making the plot you asked for is the performance of your + request (Art. 6(1)(b) GDPR); the telemetry and the rate limits rest on legitimate + interest (Art. 6(1)(f) GDPR); a feedback case rests on your explicit submission. + + what we do not collect • no user accounts or personal profiles @@ -257,8 +317,10 @@ export function LegalPage() { • no cookies at all (localStorage holds UI preferences — theme, image size — a cached release number, and, once you use the feedback widget, the random id above)
• no ai training: nothing you send is used to train ai models. - the ai in this project writes the catalogue, at build time; it never sees a - visitor's request + when you browse the catalogue, the ai in this project has already done its part: it + writes the catalogue at build time and never sees a visitor's request. only the + data assistant above sends what you give it to a model, under the terms described + there
@@ -270,8 +332,9 @@ export function LegalPage() { hosting & third parties the site, its database and its images run in the EU (Netherlands, europe-west4); - Cloudflare's network is global, and Plausible is a service of its own on its own - EU servers: + Cloudflare's network is global, Plausible is a service of its own on its own EU + servers, and the data assistant's models — used only when you use the assistant — + are called in Vertex AI's EU region: @@ -295,6 +358,13 @@ export function LegalPage() { analytics Plausible Analytics (EU, proxied) + + ai assistant + + Google Cloud Vertex AI, Claude models by Anthropic (EU) — only when you use the + data assistant + +
@@ -302,12 +372,13 @@ export function LegalPage() { you can ask what is stored about you and have it corrected, erased or its use restricted, object to the processing on grounds relating to your particular situation, - and complain to a supervisory authority. in practice that means a feedback entry or a - log line; a few lines to{' '} + and complain to a supervisory authority. in practice that means a feedback entry, an + assistant feedback case or a log line; a few lines to{' '} admin@anyplot.ai {' '} - are enough. + are enough, also to have an assistant feedback case deleted early, its files and its + row.
diff --git a/changelog.d/legal-ai-assistant.md b/changelog.d/legal-ai-assistant.md new file mode 100644 index 00000000000..2cc6d5be6e1 --- /dev/null +++ b/changelog.d/legal-ai-assistant.md @@ -0,0 +1,10 @@ +### Changed + +- **The privacy policy covers the data assistant preview.** The legal page now says, + in a part scoped to the people who use it (browsing the catalogue is unaffected), + what the admin-only "use with my data" chat does with what you give it: Claude models + through Vertex AI in the EU, in-memory sessions, the content-free and pseudonymous usage + telemetry (180 days), the HMAC-keyed attribution lines (30 days), the feedback cases + (files 180 days, the row until you ask), and a legal basis per purpose. The "the ai never + sees a visitor's request" line now separates browsing the catalogue from using the + assistant, and the processor table gains an "ai assistant" row (#12208). diff --git a/docs/reference/agent-telemetry.md b/docs/reference/agent-telemetry.md index a203170d852..782ba713c0b 100644 --- a/docs/reference/agent-telemetry.md +++ b/docs/reference/agent-telemetry.md @@ -228,9 +228,11 @@ minutes, so reloading `/debug` runs no query at all. The answer holds aggregates `not_configured` while the variable is unset, and `unavailable` when the query fails (no table yet, no permission, a timeout); both come with zeros, the card shows its empty state, and a failure is never kept. -## Before the chat opens to the public +## On the legal page The telemetry is content-free and keyed by a salted, per-instance session hash, but it is still a record of -use. Before real users reach the chat, the legal page (`app/src/pages/LegalPage.tsx`) needs an entry for it: -purpose (improving the chat), the fields in plain words, the 180-day retention, the EU location, and that -no content is recorded. The chat is admin-only on 2026-10-16, so the page stays unchanged for now. +use. The privacy section of the legal page (`app/src/pages/LegalPage.tsx`, "data assistant (preview)") +describes it: the purpose, the fields in plain words, that no content is recorded, the 180-day retention, +the EU location, and that it is pseudonymous rather than anonymous. The section describes the chat as a +preview that only admins can open, so it stays true when the chat opens. If you change a field, the +retention or the location here, update that section too; `app/src/pages/LegalPage.test.tsx` pins its claims.