From e8cd8295a56150ae02912d86fc760e278c821a6b Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:56:55 +0200 Subject: [PATCH 1/3] docs(legal): privacy section for the data assistant preview Describe the admin-only "use with my data" chat on the legal page: model provider and region (Claude via Vertex AI, EU), in-memory sessions, the content-free pseudonymous telemetry (180 days, europe-west4), the HMAC-keyed attribution lines (30 days), feedback cases (files 180 days, row until asked), a legal basis per purpose, a processor row, and a no-personal-data hint. Separate browsing the catalogue from using the assistant in the "no ai training" bullet. Pin the new claims in LegalPage.test.tsx and point the telemetry reference at the new section. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M --- app/src/pages/LegalPage.test.tsx | 34 +++++++++++++ app/src/pages/LegalPage.tsx | 80 +++++++++++++++++++++++++++---- changelog.d/legal-ai-assistant.md | 9 ++++ docs/reference/agent-telemetry.md | 10 ++-- 4 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 changelog.d/legal-ai-assistant.md diff --git a/app/src/pages/LegalPage.test.tsx b/app/src/pages/LegalPage.test.tsx index 5be53ca58c1..0d260fc9b46 100644 --- a/app/src/pages/LegalPage.test.tsx +++ b/app/src/pages/LegalPage.test.tsx @@ -93,6 +93,40 @@ 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(); + expect(screen.getByText(/it is by\s+default, untick it/)).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(); + expect( + screen.getByText('Google Cloud Vertex AI, Claude models by Anthropic (EU)') + ).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..998c0a78c2f 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 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,59 @@ export function LegalPage() { front of a request — and to absorb DDoS attacks. + data assistant (preview) + + 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. + + + 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, the + plot's code and images if one was made and, while the "# include + data.csv" box is ticked — it is by default, untick it to leave your data out — + the data file itself. 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 +311,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 +326,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 are called in Vertex AI's EU + region: @@ -295,6 +352,10 @@ export function LegalPage() { analytics Plausible Analytics (EU, proxied) + + ai assistant + Google Cloud Vertex AI, Claude models by Anthropic (EU) +
@@ -302,12 +363,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..e544f3b67d6 --- /dev/null +++ b/changelog.d/legal-ai-assistant.md @@ -0,0 +1,9 @@ +### Changed + +- **The privacy policy covers the data assistant preview.** The legal page now says + 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. 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. From 391ea5ece7638d74c73d9d46a452e956ad3ae1ef Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:57:26 +0200 Subject: [PATCH 2/3] docs(changelog): add PR reference to legal-ai-assistant fragment Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M --- changelog.d/legal-ai-assistant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/legal-ai-assistant.md b/changelog.d/legal-ai-assistant.md index e544f3b67d6..de0c30f9e93 100644 --- a/changelog.d/legal-ai-assistant.md +++ b/changelog.d/legal-ai-assistant.md @@ -6,4 +6,4 @@ 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. + assistant, and the processor table gains an "ai assistant" row (#12208). From cd40406f74ed170da557e3927bc94d79c3e99182 Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Fri, 16 Oct 2026 22:03:15 +0200 Subject: [PATCH 3/3] docs(legal): scope assistant processing to its users; renderer cache and profile sample Owner addition: the assistant part opens with a scope line (only if you use the data assistant; browsing is unaffected), and the processor row says "only when you use the data assistant". Copilot findings: name the renderer's in-memory cache of its last 16 results, which has no idle timer, and say that unticking "# include data.csv" leaves out that file only, while the profile sample (first 5 rows), the conversation, the code and the images stay in the case. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M --- app/src/pages/LegalPage.test.tsx | 11 ++++++-- app/src/pages/LegalPage.tsx | 47 ++++++++++++++++++------------- changelog.d/legal-ai-assistant.md | 3 +- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/app/src/pages/LegalPage.test.tsx b/app/src/pages/LegalPage.test.tsx index 0d260fc9b46..dd3141a569d 100644 --- a/app/src/pages/LegalPage.test.tsx +++ b/app/src/pages/LegalPage.test.tsx @@ -116,12 +116,19 @@ describe('LegalPage', () => { expect(screen.getByText(/pseudonymous, not\s+anonymous/)).toBeInTheDocument(); expect(screen.getByText(/up to 180 days/)).toBeInTheDocument(); expect(screen.getByText(/keyed hash \(HMAC\)/)).toBeInTheDocument(); - expect(screen.getByText(/it is by\s+default, untick it/)).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('Google Cloud Vertex AI, Claude models by Anthropic (EU)') + 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(); diff --git a/app/src/pages/LegalPage.tsx b/app/src/pages/LegalPage.tsx index 998c0a78c2f..4db795b06ba 100644 --- a/app/src/pages/LegalPage.tsx +++ b/app/src/pages/LegalPage.tsx @@ -191,8 +191,8 @@ export function LegalPage() { 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; the data assistant below names its own grounds. 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 @@ -248,12 +248,15 @@ export function LegalPage() { front of a request — and to absorb DDoS attacks. - data assistant (preview) + + only if you use the data assistant (preview) + - 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. + 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 @@ -266,7 +269,9 @@ export function LegalPage() { 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. + 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, @@ -286,14 +291,15 @@ export function LegalPage() { 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, the - plot's code and images if one was made and, while the "# include - data.csv" box is ticked — it is by default, untick it to leave your data out — - the data file itself. 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. + 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 @@ -327,8 +333,8 @@ export function LegalPage() { the site, its database and its images run in the EU (Netherlands, europe-west4); Cloudflare's network is global, Plausible is a service of its own on its own EU - servers, and the data assistant's models are called in Vertex AI's EU - region: + servers, and the data assistant's models — used only when you use the assistant — + are called in Vertex AI's EU region: @@ -354,7 +360,10 @@ export function LegalPage() { ai assistant - Google Cloud Vertex AI, Claude models by Anthropic (EU) + + Google Cloud Vertex AI, Claude models by Anthropic (EU) — only when you use the + data assistant +
diff --git a/changelog.d/legal-ai-assistant.md b/changelog.d/legal-ai-assistant.md index de0c30f9e93..2cc6d5be6e1 100644 --- a/changelog.d/legal-ai-assistant.md +++ b/changelog.d/legal-ai-assistant.md @@ -1,6 +1,7 @@ ### Changed -- **The privacy policy covers the data assistant preview.** The legal page now says +- **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