Privacy policy — Quickbase Buddy
Chrome Web Store review requires a privacy policy at a public URL for any
extension that handles user data. This is that text, hosted at
https://app.quickbasebuddy.com/privacy, which is rendered from this file at
build time, so this file stays the single source.
Last changed 7 October 2026 (only you can erase the change journal; AI clients no longer have a tool for it). Last reviewed in full against the code on 8 September 2026.
The short version
Buddy is in two halves and they have different answers.
The free features — form switcher, field export, role manager, form search, table home button — run entirely inside your browser, on Quickbase pages, using the session you are already signed in to. They send nothing anywhere except Quickbase itself.
The paid AI layer needs an account, so there is now a server. It holds your name, your email address, a hash of your password, and what you pay for. Nothing about Quickbase is stored on it — no realm, no app or table names, no schema, no records, no tool arguments. There is no column in its database that could hold any of that.
The sidebar chat sends your conversation — which includes what its tools read from your Quickbase tab — to the AI model you choose to chat with. With the Buddy model it passes through the server to Anthropic and is not kept on the way; with a model of your own it goes straight from your browser to that provider. See "The sidebar chat" below.
Everything Buddy remembers about your Quickbase apps — the change journal, the schema index the side panel builds — stays in your browser's own storage on your machine, and you can erase it.
What it does
Buddy adds buttons and a side panel to Quickbase pages. With a paid plan it also edits Quickbase for you — fields, tables, relationships, records and table reports — driven by an AI client on your own computer or by the chat in its side panel. Either way it acts inside a Quickbase tab you already have open, the same way you would if you clicked the buttons yourself.
What runs automatically, and where
Six content scripts are declared in the manifest and Chrome runs them on
matching Quickbase pages without being asked. A seventh is injected by the
extension's service worker. All seven are in apps/extension/entrypoints/.
| Script | Runs on | When |
|---|---|---|
apiRelay.content.js |
every https://*.quickbase.com/* page |
document_start |
formSearchTab.content.js |
every https://*.quickbase.com/* page |
document_idle |
homePageReturn.content.js |
86 table- and app-settings URLs | document_idle |
fieldRoleManager.content.js |
10 modify-field / role-permission URLs | document_idle |
fieldExport.content.js |
4 field-list URLs | document_idle |
roleUserInterface.content.js |
4 role table-UI URLs | document_idle |
formSwitcher.js |
12 record-form URLs | injected by the service worker |
Only apiRelay and formSearchTab state a time explicitly; the other four take
Chrome's default, which is document_idle. The two that match every page of
your realm do so because the features they carry — the side panel's data relay
and the form-search launcher — have to be ready wherever you happen to be.
formSwitcher.js is not a declared content script. The service worker injects
it with chrome.scripting.executeScript when chrome.webNavigation's
onCompleted or onHistoryStateUpdated fires on one of those record-form
URLs — the second event is why the webNavigation permission is needed at all,
because Quickbase's single-page navigations are silent to tabs.onUpdated
(apps/extension/lib/legacyPanel.ts).
Three of these send network requests, and every one of them goes to Quickbase:
apiRelay.content.jscallsapi.quickbase.com— the temporary-token endpoint and then the app, tables, fields and reports endpoints, on behalf of the side panel.formSwitcher.jscalls two of Quickbase's own private endpoints to list a table's forms:https://<realm>.ui.quickbase.com/ui/api/db/<table>?a=JBI_GetFormsandhttps://services.quickbase.com/api/forms/graphql.- The service worker fetches the account site, and only for the entitlement — see "The account" below.
The other four make no network requests of any kind. fieldExport builds its
CSV in the page and hands it to the browser as a data: URI, so the export
never travels anywhere; fieldRoleManager and roleUserInterface only set
form controls; homePageReturn only adds a link.
One thing that changed from v1. The shipped v1 apiRelay sent a Quickbase
app token — the author's own, for one of the author's apps — as plain text
in its source, on every temporary-token request. Quickbase only reads that
header on apps whose admin requires application tokens, so it did nothing for
anyone else's app, and it was a secret in a public bundle. 2.0 does not send
it (apiRelay.content.js, 7 September 2026). The request works on your
signed-in session alone, the same way the AI tools' requests always have.
How it signs in to Quickbase — it doesn't
Buddy has no Quickbase account, no Quickbase password, no API key and no admin token. It never asks you for Quickbase credentials and has nowhere to put them if you offered.
Instead it borrows the session you already have. When it needs to talk to Quickbase, it runs a request inside one of your open Quickbase tabs, so the request carries that tab's cookies. Its first call asks Quickbase for a short-lived token of its own:
GET https://api.quickbase.com/v1/auth/temporary/<app or table id>
This is the same temporary-authorization endpoint the Quickbase web UI uses for
its own requests. The token that comes back is held in memory only — four
and a half minutes in the service worker's cache
(packages/qb-core/src/tempToken.ts), five minutes in the content script's
(apiRelay.content.js) — and is never written to disk. Restarting your browser
or reloading the extension discards it.
The consequence is that Buddy can never do anything in Quickbase you could not do yourself, and it stops working the moment you sign out. If no Quickbase tab is open, or that tab is signed out or expired, Buddy fails with an error and does nothing.
Relevant code: packages/qb-core/src/tempToken.ts,
packages/qb-core/src/tabFetch.ts,
apps/extension/entrypoints/apiRelay.content.js.
The account, and what the backend stores
The paid layer needs to know who you are and what you pay for, so there is a server: a Cloudflare Worker on a D1 database, which also serves the account site. It is described in full in docs/backend.md.
It was deployed on 7 September 2026 and runs at
https://app.quickbasebuddy.com — a Cloudflare Worker on a Cloudflare D1
database, in a Cloudflare account belonging to Jaybird Technologies. The
account site is the same Worker on the same origin.
The only data in it so far is the developer's own test account and the sandbox subscription created to prove the payment path works; it has not yet held anybody else's. Everything below describes what it stores for a real account from the moment there is one.
What it stores
Eight tables, from apps/api/migrations/:
user— your name, your email address, anemailVerifiedflag, an optional image URL, and timestamps. The email is unique; it is how you sign in.account— your password, as a scrypt hash, never in clear text. (The table has OAuth columns too, because it is the authentication library's own schema; there are no OAuth sign-ins in this version and those columns stay empty.)session— one row per sign-in: an opaque session token, an expiry, and the columnsipAddressanduserAgent. Those two are populated by the authentication library's own defaults, and nothing in this repository turns them off. So a sign-in records the IP address and browser user-agent string it came from. The IP is the one Cloudflare puts in itsCF-Connecting-IPheader. The deployed instance has not been inspected to confirm what those two columns actually contain; this is the behaviour the schema and the configuration imply.verification— one row per outstanding password-reset link: a random token and your user id, expiring after an hour and deleted when the link is used. Email-confirmation links are signed rather than stored, so they add nothing here.stripe_customers— your user id and your Stripe customer id.subscriptions— the Stripe subscription id, its status, the plan, and when the current billing period started and ends.usage_events— one row per call to the Buddy model (below): your account id, the kind (llm_turn), the cost in tenths of a cent, the model's name and the time. It is what the allowance is counted from, over your current billing period. It is deliberately built with no payload column, so it records that a call happened and what it cost, never what it was about.rateLimit— request counters for the sign-in, sign-up and email-sending endpoints (see "Email" below): an IP address and the endpoint's path, a count and the time of the last request. No account id. A row whose minute is over is deleted the next time the limiter prunes, which later requests trigger; on a quiet day one can sit until then.
What it does not store
Nothing about Quickbase. No realm, no app or table names, no field or report definitions, no records, no tool names or tool arguments, no Quickbase session or token of any kind. The schema has no column that could hold any of it. Tool calls in AI mode travel from your AI client to a local process to the extension to your own Quickbase tab, and at no point touch this server. The Buddy model below is the one route where Quickbase content passes through this server, and it is not written down on the way.
Stripe
Payment is handled by Stripe. Card details are entered on Stripe's own hosted
Checkout page and never reach this server or the extension — the Worker
creates a Checkout Session and sends your browser to Stripe. What the Worker
hands Stripe is your email address and your internal user id
(apps/api/src/billing.ts), so that the subscription can be matched back to
your account. Stripe's own privacy policy governs what it does with that.
The Worker learns about payments through Stripe's webhook, which it verifies by signature before applying.
Not switched on yet. Until it is, the deployed Worker sends no email at
all and the verification table stays empty. What follows is what it does
once it is on.
The Worker sends you two kinds of email and no others: a link to confirm your
address when you create an account (or sign in before confirming it), and a
link to reset your password when you ask for one. There is no newsletter, no
marketing mail and no mailing list. The mail goes out through Cloudflare
Email Service, from noreply@quickbasebuddy.com, so Cloudflare handles your
address and the message to deliver it; Cloudflare's privacy policy governs
that. The Email Service dashboard can keep a preview of sent messages for
about seven days, which would include the link in them.
(apps/api/src/mail.ts, apps/api/src/auth.ts)
Separately, and on whether or not email is: to slow down anyone trying to
flood these emails or guess passwords, the sign-in, sign-up and email-sending
endpoints count requests per IP address, in the rateLimit table above. Page
loads that only check whether you are signed in are not counted.
The sidebar chat, and the Buddy model
The side panel's Chat tab lets you ask about, and change, the Quickbase app
you have open in plain language. It needs the Pro plan whichever model
answers; without it the tab says so and runs nothing
(apps/extension/lib/chat/access.ts). Every change it proposes is shown to you
first and written only after you click Approve.
By default the chat answers with the Buddy model, which is included in the
Pro plan up to a monthly allowance that renews on your billing date. The side
panel sends the conversation to POST /llm/v1/messages on the Worker, carrying
your entitlement token instead of a password. The conversation is your messages, the chat's instructions and
tool list, and the results of the tools it ran — which can include app, table,
field and report names, report settings, and record values a tool read from
your Quickbase tab. The Worker forwards it to Anthropic's API and returns
the answer. It does not store or log the conversation; it writes only the
usage_events row above (apps/api/src/llm.ts). Anthropic receives the
conversation under its own commercial terms and privacy policy. To show how
much of the allowance is used, the panel also reads GET /llm/usage with the
same token; that answers with your spend, the cap and the period's dates, and
receives nothing.
The chat can instead use a model of your own: Anthropic or OpenRouter with
your own API key, a local model in Ollama or LM Studio, or another
OpenAI-compatible service you name (apps/extension/lib/chat/llm.ts). Then
the extension talks to that service directly and nothing goes through this
server. The provider, model and key you choose are kept in
chrome.storage.local under qbb:chatSettings. Conversations are saved on
your computer, in the extension's own IndexedDB, so the panel can reopen them —
see "Chat history" under "What is stored on your computer". On a development
build each line is also written to the connection log described there.
Request logging
apps/api/wrangler.jsonc sets observability.enabled with
head_sampling_rate: 1. That switches on Cloudflare Workers Logs at 100%:
every request to the account site and API is logged by Cloudflare with its
standard request metadata, which includes the URL path, method, status, timing
and the client IP address. Retention is whatever Cloudflare's plan gives; it is
not configured here.
The Worker's own code writes six log lines, all of them error paths, all of
them a JSON object with a stage name and an error message
(apps/api/src/index.ts, and apps/api/src/mail.ts for a failed send, which
logs the subject line and the failure code but not the recipient). None of
them logs a user id, an email, a token or a request body on purpose. The catch-all handler does log the exception's stack
trace, which could incidentally carry a value that appeared in an error message.
There is no analytics, no product telemetry, no crash reporting and no
tracking of any kind in the extension, the bridge, the account site or the
Worker. There is no analytics SDK anywhere in the dependency tree, no tracking
pixel, and no sendBeacon call. Cloudflare's request logging above is the one
thing in this document that is a log of you rather than of your data, and it is
infrastructure, not a decision to measure you.
The entitlement token
The extension needs to know whether you have paid, and it needs to know it offline. So the account site hands it a signed statement rather than a session.
- You sign in on the account site and click Connect extension.
- The page reads
GET /entitlementwith its own first-party cookie, and sends the token only to the extension withchrome.runtime.sendMessage(extensionId, …). Your session cookie never crosses; the extension never calls the sign-in API. - The extension verifies the signature against a public key compiled into it, and caches the result.
The token is a JWT whose claims are: an opaque account id (the same
32-character id the database uses — not your email), the plan name, one feature
flag (mcp), and issued-at and expiry timestamps. It lives 24 hours. There is
no Quickbase data in it and no personal data beyond that id
(apps/api/src/jwt.ts, packages/qb-core/src/entitlement.ts).
- Cached in
chrome.storage.localunderqbb:entitlement, as the raw token, the verified claims and the time it arrived (apps/extension/lib/entitlement.ts). - Refreshed opportunistically: when the service worker starts and the cache
is more than 12 hours old, it fetches
GET /entitlementfrom the account origin withcredentials: "include", carrying your account-site cookie. Chrome lets that request through only if you have granted the optional permission for the account site — Allow automatic renewal on the Options page (apps/extension/lib/accountPermission.ts). Without it the request is refused, and clicking Connect extension on the account site is how the token renews. A failure is logged and ignored. - Trusted offline for 72 hours past expiry, so a laptop with no network still reaches its own Quickbase.
The manifest's externally_connectable lists exactly one origin, and only a
page on that origin can send the extension anything at all. The listener checks
the sender's origin a second time itself and answers nothing else
(apps/extension/lib/entitlementListener.ts). That origin is
https://app.quickbasebuddy.com, the deployed account site.
The development bypass. The code contains a setting that skips the
entitlement check entirely. It is honoured only when the extension has no
update_url in its manifest, which is Chrome's own marker for "loaded unpacked
from a folder" (apps/extension/lib/buildKind.ts). A Web Store install reads
the same stored value and discards it, and the options page renders its toggle
only on an unpacked build. It exists so that a development build can be tested
without a paid subscription. It is in the shipped code and inert there, and
that is why it is written down.
Where data goes
Only to your own Quickbase realm, and to the account site if you have an
account. The extension is allowed to reach exactly two kinds of address for
Quickbase, and Chrome enforces this — they are its declared host_permissions:
*://*.quickbase.com/*— your realm, plusservices.quickbase.comand<realm>.ui.quickbase.com, both Quickbase's own hosts*://api.quickbase.com/*— Quickbase's REST API
Plus one optional one, https://app.quickbasebuddy.com/*, the account site. It
is not granted at install; only if you click Allow automatic renewal on the
Options page does the extension send it your account-site cookie, for the
entitlement refresh above. The chat reaches the same site without that
permission, for the Buddy model and its allowance: those requests carry the
entitlement token rather than a cookie, and the Worker answers only this
extension's own origin.
And one loopback address: the legacy WebSocket transport dials
ws://127.0.0.1:<port> (apps/extension/lib/bridgeClient.ts). It reaches only
your own machine, and the default transport is native messaging, which uses no
socket address at all.
Nothing else, with one exception you choose: if you set the chat to use a
model of your own, it sends the conversation to that service — Anthropic's API,
OpenRouter, a local Ollama or LM Studio, or an address you type in — with the
key you gave it. No other third-party host appears in any request it makes.
The account site itself loads no
third-party resource — no analytics tag, no font from a CDN, no Stripe.js, no
framework from anywhere. Each of its pages loads one stylesheet, at most one
script, and the two typefaces that stylesheet names, all from their own origin:
the fonts are files the site ships, copied out of their npm packages when it is
built (apps/web/scripts/sync-design.mjs) rather than fetched from anyone at
run time. The site has no runtime dependencies at all.
One third-party address does appear in the product: the side panel has a
Submit feedback link to a Zoho Forms page (FEEDBACK_URL in
apps/extension/entrypoints/sidepanel/tools/panelConstants.jsx). Nothing is
fetched from it and no request is made until you click it, at which point you
are on Zoho's site under Zoho's terms.
What is stored on your computer
Five things, all local, none of them synced or uploaded.
1. The change journal
Every change the AI layer applies is recorded, so you can see afterwards exactly what happened — including anything that moved that you did not ask to move. This is a safety feature: it is how Buddy catches itself making an unintended change.
- Where: an IndexedDB database named
qbb-journal, inside the extension's own storage in your browser profile. - What: for each change — the time, your realm hostname, the app and object it touched, the diff you approved, and the object's complete state before and after. For a record edit, that "before" state is the actual values of the records that were changed. For a report edit it is the whole report definition, including the builder form body.
- How long: entries older than 90 days are removed, and only the most recent 500 are kept, whichever bites first.
- Erasing it: the options page's What Buddy changed card has an Erase history button, which asks for confirmation and then deletes everything. Only you can erase it: no AI client and not the chat has a tool for it, because the journal is what Revert works from. Uninstalling the extension removes it too.
Retention constants: packages/qb-core/src/journal/policy.ts
(MAX_AGE_DAYS = 90, MAX_ENTRIES = 500).
2. The side panel's app index
This is a free-tier feature, and it is the largest store of Quickbase data on your machine after the journal.
The side panel has an Add this app button. Adding an app writes the app's
id, your realm subdomain and a timestamp under qbb:apps, then reads the app,
its tables, every field definition in every table and every report
definition in every table through apiRelay.content.js, and writes the lot
under qbb:appIndex (indexApp in apps/extension/entrypoints/sidepanel/App.jsx;
the keys and the storage calls are in sidepanel/tools/appStorage.jsx). That is a
full local mirror of the app's schema — names, ids, types, comments, report
definitions. It is not record data.
- Where: the side panel page's own
localStorage, which belongs to thechrome-extension://origin. On disk, in your browser profile, unencrypted, and never synced between machines. - How long: indefinitely. Nothing prunes it and there is no age or size cap. It is rewritten when you press Sync.
- Erasing it: the side panel's Manage apps view removes an app, and removing it deletes both its registration and its whole index. Uninstalling the extension removes everything.
3. Settings, the connection log, and the entitlement
In chrome.storage.local, in your browser profile
(apps/extension/lib/settings.ts, apps/extension/lib/entitlement.ts):
- Settings (
qbb:settings) — whether the bridge is enabled, which transport to use, whether you chose light, dark or your system's theme, the development bypass flag, and a pairing token for the older WebSocket transport. That token is a local shared secret, stored as plain text; it is not a Quickbase credential and is unused on the default transport. - Connection statistics (
qbb:bridgeStats) — counters and timestamps. - A connection log (
qbb:bridgeLog) — the last 1000 events: connects, disconnects, heartbeats, errors, entitlement outcomes, and each tool call. - The entitlement (
qbb:entitlement) — the signed token, its claims, and when it arrived, as described above.
Two things worth knowing about these.
A tool-call entry in the connection log records the tool's name and its full
arguments, serialised as JSON (apps/extension/lib/toolDispatch.ts). When the
tool is one that writes records, those arguments contain the record values you
are writing. So this log is a second place record data can land, and it has
different rules from the journal: it is capped at 1000 entries with no age
limit, and the journal's Erase history button does not touch it. It has
its own Clear control on the options page, next to the log.
The cached entitlement carries your opaque account id. It is deleted when
you sign out on the account site — the page tells the extension before it ends
the session (apps/web/src/account.ts, apps/extension/lib/entitlementListener.ts)
— or when you click Forget this plan on this browser on the Options page.
Otherwise it stays in chrome.storage.local; it stops working as a credential
after 24 hours plus the 72-hour grace.
Both of these are more than a privacy policy would ideally have to describe. They are written here because they are what the code does.
4. Which tabs have the panel open
In chrome.storage.session, one boolean per tab id, under
panelVisible:<tabId> (apps/extension/lib/legacyPanel.ts). It holds no URL
and no content, it is removed when the tab closes, and session storage is
memory-backed and cleared when the browser restarts.
5. Chat history
Each chat conversation is kept in the extension's IndexedDB database
qbb-chat (apps/extension/lib/chat/history.ts): its title (the first thing
you asked), when it was started and last used, which model answered, and every
message — yours, the model's, and the results of the tools it ran, which can
include app, table, field and report names and record values. The system prompt
is not stored. At most the last 30 conversations are kept, and one unused for 90
days is deleted. Nothing in it is sent anywhere by being stored; continuing a
conversation sends it to the model you have chosen, as described above.
6. The bridge host log
If you use Buddy with an AI client, a small local helper process runs on your
machine and writes a log to /tmp/quickbase-buddy-<your user>/host.log
(%LOCALAPPDATA%\quickbase-buddy-host.log on Windows), created readable only by
you (mode 0600) and truncated once it passes 1 MB.
This log records connection lifecycle only — hosts starting and stopping, clients connecting and disconnecting, framing errors. It does not contain tool names, tool arguments, Quickbase data, or the contents of any request.
The local helper process
To let an AI client such as Claude Code drive it, Buddy uses a helper called
qbb-bridge. It matters for privacy that this is entirely local:
- Chrome starts it, using Chrome's native messaging mechanism, and stops it when the extension goes away. It is not a server you run or expose.
- It listens on a Unix domain socket in a directory only your user account
can open (directory mode
0700, socket mode0600), named for its own process id —/tmp/quickbase-buddy-<user>/<pid>.sock. On Windows it is a named pipe. It does not open a network port, so nothing on your network or the internet can reach it. - Chrome only allows the connection because the host's manifest names this
specific extension in its
allowed_origins. - It installs itself under
~/.quickbase-buddy/: a shim script, and the host bundle inbridge/<version>/.
One thing it reads that is worth naming. To find Buddy's extension id
without asking you to read it off chrome://extensions, the bridge's
registration step opens each installed browser profile's Secure Preferences
(or Preferences) file and walks the list of every extension you have
installed — across Chrome, Chrome Beta, Dev and Canary, Chromium, Edge, Brave,
Vivaldi, Arc and Opera (packages/mcp-bridge/src/extensionDiscovery.ts). It is read-only;
it keeps only entries that declare the nativeMessaging permission — in either
permissions or optional_permissions, since Buddy declares it in the second —
and whose name begins "Quickbase Buddy", and only those ids are ever written
into the host manifest. Nothing about your other extensions is stored,
transmitted or logged. But the file is read, and that is worth saying.
It passes tool calls through to the extension and answers back. Nothing it handles leaves your machine.
How this is built and why is described in docs/native-host.md.
Browser permissions, and why each one
Everything in this table is required — granted when you install the
extension — except nativeMessaging and the account site, which are
optional: neither is granted at install, and Buddy asks for each only when
you click the button on the Options page that needs it.
| Permission | Required? | What it is for |
|---|---|---|
*://*.quickbase.com/* |
Required | Reading and writing Quickbase, on your realm and on Quickbase's services. and .ui. hosts. |
*://api.quickbase.com/* |
Required | Quickbase's REST API, with a temporary token. |
tabs |
Required | Finding your open Quickbase tab so requests can run in it, and reading a tab's URL to decide whether the side panel applies to it. |
scripting |
Required | Injecting formSwitcher.js into a record form, and running the side panel's search and highlight functions in the page. |
webNavigation |
Required | Quickbase's single-page navigations do not fire tabs.onUpdated, so the form switcher would not appear without onCompleted and onHistoryStateUpdated. |
sidePanel |
Required | Opening and configuring the side panel. |
storage |
Required | The settings, log and entitlement above, and the per-tab panel flag. |
nativeMessaging |
Optional | Talking to the local helper process. Asked for by a click on the Options page, at the moment you enable the bridge; until you grant it, the extension does not try to start the helper at all. |
https://app.quickbasebuddy.com/* |
Optional | Renewing the entitlement from the account site with its cookie, so you need not click Connect extension each day. Asked for by Allow automatic renewal on the Options page. |
externally_connectable |
Required | One origin — the account site — may send the extension its entitlement token. Nothing else may message it. |
v1's activeTab is not carried over. The host permissions already cover
every URL either half touches, so it granted nothing and only widened the
install prompt.
Why nativeMessaging is optional. Chrome shows a warning for it —
"Communicate with cooperating native applications" — and an extension that
adds a warning is disabled until every existing user accepts it. Most people
who use Buddy never turn the bridge on, so making it optional is what lets 2.0
arrive as an ordinary silent update instead of stopping everyone at a prompt
for a feature they do not use. The practical effect for you: if you never
enable the bridge, you are never asked for this permission and the extension
never holds it. If you do enable it and then decline, nothing breaks — the
bridge simply stands down, and the Options page keeps an Allow native
messaging button for whenever you change your mind. You can withdraw the
permission afterwards from Chrome's own extension settings, and the extension
closes the connection when you do.
Two exceptions worth stating plainly, because both involve pages that are
not Quickbase. The tabs permission lets Chrome tell Buddy the URL of every
open tab, and two things use it beyond Quickbase.
The service worker reads the URL of every page you load. It listens on
chrome.tabs.onUpdated with no URL filter, and on every completed navigation in
any tab it looks at that tab's address to decide whether to enable the
extension's icon and side panel (enableSidePanelForTab in
apps/extension/lib/legacyPanel.ts). If the page is not on a Quickbase
subdomain it disables the icon and does nothing further: the URL is not stored,
not sent anywhere, and not written to any of the logs described above. It is
written to the extension's own service-worker console —
console.info("[QuickbaseBuddy] Disabling action for non-Quickbase URL", …) —
which is visible only to someone who opens developer tools on the extension, and
is not persisted to disk. This is v1 behaviour, and it is the price of an icon
that lights up only on Quickbase.
get_current_view reads the active tab's URL, whatever site it is on. The
tool answers "which app and table am I looking at?", and to decide whether you
are on Quickbase it has to look at the address first. If you are not on
Quickbase the URL is discarded — the tool reports only "the active tab is
not on the Quickbase realm", and no non-Quickbase address appears in its output,
in the journal, or anywhere else. Buddy still cannot read the contents of such
a page.
And one about pages that are Quickbase. The side panel's form search runs
its search function (apps/extension/entrypoints/sidepanel/tools/pageSearch.jsx)
in the page's main JavaScript world (world: "MAIN", in runSearch in
apps/extension/entrypoints/sidepanel/App.jsx), not in the isolated world
content scripts normally get. It does that because on the new form builder the
field labels are only in the page's own Apollo GraphQL cache, which an
isolated-world script cannot see. It reads form and section names, field labels,
and on legacy form pages the decoded default values of the form's hidden inputs.
The results are shown in the panel and are not stored or transmitted. It is a
wider reach into the page than a content script has, and it is only ever pointed
at a Quickbase form.
What Buddy never does
- It never sends anything about your Quickbase apps — schema, records, tool arguments, realm — to the author or to any third party, with one exception that is yours to use or not: a chat conversation goes to the model you chose for the chat, as described in "The sidebar chat, and the Buddy model". The account server keeps none of it, and has no column that could hold it.
- It never asks for a Quickbase password, API key, user token or admin token, and has nowhere to put one.
- It never sees a card number. Payment details are entered on Stripe's own hosted page.
- It never sells or shares your personal information, and never uses it for advertising, profiling, or any purpose other than running your account.
- It never reads your browsing history, and never loads or reads the contents of a non-Quickbase page. It does read the address of tabs as they load, for the two reasons above.
- It never deletes anything in Quickbase that it did not create. There is no delete tool of any kind: no delete field, table, report, relationship or record. The one exception is undoing Buddy's own change: when you revert a change that added a field, and you approve that revert, Buddy removes that field. It does this only if the field still holds no data and no formula uses it.
If you have an account, then the things it does do with personal information are: store your name, email and password hash; record the IP address and user-agent of each sign-in; give your email and account id to Stripe so billing works; send confirmation and password-reset emails to your address through Cloudflare Email Service; pass a conversation you send to the Buddy model through to Anthropic, keeping only its cost; and put your opaque account id inside the entitlement token cached in your browser. That is the whole list.
Erasing your data
On your computer:
- The journal: options page → What Buddy changed → Erase history.
- The connection log: options page → Clear, beside the log.
- The app index: side panel → Manage apps → remove the app.
- Chat history: the chat's History → Delete beside a conversation, or Clear all.
- The cached entitlement: sign out on the account site, or options page → Forget this plan on this browser.
- Everything: remove the extension at
chrome://extensions. That deletes its IndexedDB, itschrome.storageand its pagelocalStoragein one go. Delete/tmp/quickbase-buddy-<user>/to remove the host log, or just restart — it lives in a temporary directory. Runqbb-bridge unregisterand remove~/.quickbase-buddy/to take away the helper and its registration.
On the server:
- There is no self-service account deletion. The Worker defines no route
that deletes a user (
apps/api/src/index.ts), and the authentication library's own delete-user feature is not switched on in its configuration (apps/api/src/auth.ts). This policy will not pretend otherwise. Until there is a route, ask at the contact address below and it will be done by hand. Deleting a user row cascades to that user's sessions, credentials, Stripe customer mapping and subscription rows. - Cancelling a subscription is self-service, through the Stripe Customer Portal, from the account page.
- Cloudflare's request logs expire on Cloudflare's own schedule and are not individually addressable.
Children
Buddy is a tool for working with a Quickbase business application. It is not directed at children and collects nothing from anyone under 13.
Changes to this policy
If Buddy ever gains a capability that changes any of the above, this document changes with it in the same commit, and the date at the top moves.
Contact
Questions about this policy, or a request to delete your account: email jtorrence@jaybirdtechnologies.com.