What Boosthis collects inside your app

For the person who has to describe us in your privacy policy. Last updated 2026-10-16.

When you install Boosthis, our software runs inside your application: it measures how your own code behaves and sends readings back to us. That makes us a third party processing data inside your product, and it is something your users can reasonably expect your privacy policy to mention.

Only we can say accurately what our software does inside your app, so this page says it — what the kit sends, what it never sends, what changes when you switch something on, where it is processed, how long it is kept, and what happens when one of your users asks to be forgotten. At the bottom there is a paragraph you can paste into your own policy.

This page describes how our software behaves. It does not tell you what your own policy needs to say — that is yours to decide, with your own advisers. Everything here restates our published Privacy Policy in the words your reader needs, and a check fails our build if the two ever describe different data.

What the kit sends

Every registered install sends these, in every runtime, whatever else you have switched on. "Registered" means a Boosthis project key is configured — an install with no key never sends anything at all.

  • A random install identifier — A random UUIDv4 generated on the machine your app runs on, the first time it registers. It is made from nothing about your users, their devices, an IP address or a hostname. Boosthis links it to the project key it registered with, and so to the account that holds that key: that link is how your dashboard, alerts and AI connections show you your own installs.
  • Buckets describing a recurring performance problem — When the same performance pattern recurs, the kit sends the kind of problem it detected, a three-level severity bucket and a coarse occurrence-count bucket. The screen or route name is never part of that signature. The Node and browser kits send it beside the signature: each of their submissions says where the latest sighting was — the code-defined route or screen label, put through the same identifier guard as every label, or, where none can be named, a reason from a fixed list (no screen or route was open, the kit does not track them there, or the label was refused). The other kits send no screen or route with a signature.
  • Buckets describing when one of those problems improves — When a tracked problem gets better, the kit sends the rule and its kind, the before-and-after rating bucket — good, needs-work or poor — how many times it was seen, and the severity and count buckets it was proven under. It is the fact that something improved, never the change that improved it: no source code, diff, file path, screen name, duration or value.
  • Error types with a redacted code location — When your app hits an uncaught error, the kit sends the error type, a redacted code location (function name, file basename and line number — never a path, URL, argument or value), a hash derived only from those code-defined tokens, and how many times it recurred. Every crash report, always on and not only in detailed crash mode, also says where in your app it happened: the code-defined label of the screen or route that was open, put through the same identifier guard as every label, or, where none can be named, a reason from a fixed list (no screen or route was open, the kit does not track them there, or the label was refused). Where the crash happened in the middle of an action the kit was tracing, it also carries that action's trace reference: the 32-character trace ID and, where there is one, the 16-character ID of the step in flight. A kit may also state when each crash happened; Boosthis keeps that time only when it is plausible, and otherwise dates the crash by when it arrived. On Android 11 and later and iOS 15 and later, the kit also reads the operating system's own record of a native crash or an "app not responding" kill at the next launch and sends only which it was, the signal or exception-type name, a fixed placeholder for the code location and when it happened — never the system's description, process or thread names, or the native stack. An error your app caught and survived is not sent as a crash by the phone and browser kits. The browser kit decides on the visitor's device, before any address is cut back, whether an error came only from a browser extension, and recognises the browser's own ResizeObserver notices; neither is sent as a crash. Of those it sends only two counts — how many of each it set aside in the crash window — and never an extension's identifier, name or file. An error whose details the browser hid, because its script came from another address, is still sent as a crash, under a fixed stand-in type. A crash that cannot be sent at once waits on the device: the React Native, Flutter, Android and Apple kits keep an unsent crash in the app's own storage, offer it again at each later launch and drop it unsent once it is 30 days old; the browser kit keeps one in the site's local storage, retried and dropped after 30 days, beside a short crash history — for each time the site was opened in the last 24 hours, at most 32 of them, when it began and was last in use, the times of its crashes and how many errors from browser extensions were set aside, times and counts only, each dropped after 24 hours.
  • Where a crash came from beyond its top frame — A crash report may also say that no stack came with the error at all; which part of the framework caught it, as a word from a fixed list (a part the kit does not know is sent as "other"); for a Flutter debug or profile build, the file basename and line of the widget the framework named as the cause; the first frame in your app's own code, in the same function name, file basename and line form, when the top frame is in the framework, the language SDK or another package; and that it came from a debug build. Which packages are your app's is decided on the device, and that list is never sent. Never the widget's description, a path, an argument or a value.
  • A short-lived crash-batch fingerprint — A current kit also sends a non-readable fingerprint for each crash batch so a retry after a lost response cannot add the same occurrences twice. It is operational duplicate-suppression metadata, not customer crash history: it is excluded from customer exports, retained for no more than 30 days and 1,024 receipts per install, and erased with the install.
  • Anonymous notes about patterns we have no rule for yet — When the kit meets a slow or buggy pattern it has no rule for, it may send the runtime, a coarse category, a severity bucket, a timing bucket, a hash and a recurrence count. Buckets and a hash — never a screen name, a value or your source.
  • The kit version and the runtime it runs in — For example, the Boosthis package version together with the language runtime, so a reading can be interpreted against the version that took it.
  • Where a server runs — When a server kit registers, the hosting platform it recognised from the markers that platform publishes about itself, as a word from a fixed list; the region code the platform declares, such as us-east-1, as the platform wrote it; and whether this is production, a preview or development — or unknown, where the platform does not say. It is what keeps a preview's numbers from being shown as your live app's. Never a hostname, an address, or any other value read from the machine.
  • The version of your own application — On a phone, the kit reads the version your app was built with — CFBundleShortVersionString on iOS, versionName on Android — and sends it alongside the Boosthis version, never merged into it, so a reading can be attributed to the release of YOUR app it came from. It is the version string you ship and nothing else: not the build's contents, not its signing identity, not a build number, not anything about the device.
  • The releases your project declares — When a release of your app goes out, your app, a deploy script, a build step or you on your dashboard can tell Boosthis so, from any runtime: the version string as given (at most 60 characters, screened like every other name), when it shipped, how it was declared — through the install's own credential, on the dashboard, read out of a phone build, or handed to the kit by your app — and when it was first and last declared. Declaring the same version again moves only that last time, and an install holds at most 200. Nothing about what the release contains. A developer can withdraw one, and all of them are deleted when the project is erased or the account is closed.
  • The names of scheduled jobs, and how each run went — If your app runs work on a schedule, the kit sends the job's name as written in your source, how long each run took, whether it succeeded, and — if your code states one — how often it should run. Never an argument, a payload, a result, a log line or anything the job touched.

What changes if you switch something on

These are the claims that are true for some customers and false for others. Check each one against your own app before you repeat it to your users.

If full performance details are on (a new project starts with them on)

Depends on you On from a new project's first check-in. Starting the kit with your project key turns them on, and the Full telemetry switch on your Boosthis dashboard turns them off from the app's next launch. The edge kit has a single mode and ignores that switch; it sends them whenever it runs with a key.

  • The route or function label — The code-defined name of the route or function you instrumented — for example /api/users or get_orders. Our guard refuses to transmit the field if it contains an email address, IP, token, phone number or other identifier, and drops the whole sample instead. If you put user content in your route names, this is the field it would reach.
  • How long it took — A duration in milliseconds for that route or function.
  • A rating bucket for the timing — One of good, needs-work or poor.
  • A rule identifier, when you attached one — If you attached a Boosthis rule to the sample, the identifier of that rule.

How to tell: Open the project on your Boosthis dashboard: its Full telemetry switch shows whether they are on, except in the edge kit, which sends them whenever it runs with a key. (The meter snapshot below has a second switch of its own, so check that separately.)

If the page map is on, and full details or meter sharing is on

Depends on you In the browser kit from version 1.0.0-alpha.144, and in the React Native kit from version 1.0.0-alpha.297, it is on unless your code passes sendPageMap: false in the kit's options; an older React Native kit sends it only if your code passes sendPageMap: true. Nothing we send back can turn it on or off. With it on, the map travels with the meter snapshot, so it also needs one of that snapshot's two switches: your code calling enableTelemetry(), or you switching on meter sharing for that project. disableTelemetry(), erasing the install, and BOOSTHIS_DISABLED=1 each stop it.

  • The page map: which of your screens leads to which — In the browser and React Native kits only: on by default in the browser kit from version 1.0.0-alpha.144 and in the React Native kit from version 1.0.0-alpha.297, and on an older React Native kit only if your code switched it on. As people move around the app, the kit counts how often each pair of route labels follows the other and sends the counted pairs — so a map of your own screens can be drawn. It is a running tally, not a recording: no session, no order of visits and nothing about who navigated. The name or caption of the control that was pressed and the gesture used are never sent, and there is no field in the upload that could carry them. The labels themselves go through the same PII guard as the route label above.

How to tell: Search your own source for sendPageMap, and check the kit's version. In a browser app on kit 1.0.0-alpha.144 or later, or a React Native app on kit 1.0.0-alpha.297 or later, the map is sent unless it says sendPageMap: false; on an older React Native kit, no map is sent unless it says sendPageMap: true, whatever else is on. Where the map is on, check the same two switches as for the meter snapshot: enableTelemetry (or enable_telemetry) in your source, and meter sharing on the project in your Boosthis dashboard — either of those means the map is being sent.

If full performance details OR meter sharing is switched on

Depends on you Either switch uploads it: your code calling enableTelemetry(), or you switching on meter sharing for that project so a connected AI can read your live numbers. disableTelemetry(), erasing the install, and BOOSTHIS_DISABLED=1 each stop it.

  • The numbers behind the health meters — One small snapshot of your dashboard meters — the boot ladder, Speed, Smoothness, Scroll, Stability, Render, Frustration and Idle, plus the runtime-specific ones such as event-loop lag, memory growth and worker health. Only the latest snapshot per project is kept. New meters ship regularly, and every one of them stays inside the three things below.
  • A score and a rating bucket for each meter — A score from 0 to 100, and one of good, needs-work or poor. Even the one-line caption a kit computes for its own on-device display is dropped at the door and rebuilt on our side from the numbers.
  • Plain numbers — counts, timings, rates and sizes — For example: milliseconds spent loading modules before startup finished, kilobytes of memory growth per request, worker restarts per hour, queue and lock waits, garbage-collection pauses, pending task counts, or how many errors were logged in the window. The server keeps a fixed allowlist of numeric meter fields, drops any field not on it, and rejects any allowlisted field whose value is not a plain number.
  • Nothing else in the snapshot — No message text, no log lines, no file, module, thread, worker or command names, no source code, no values from your app, and nothing about your users.
  • An error meter counts errors; it never carries the log — We receive how many errors your app logged and survived during the window, and the rate per hour — never the message, the log line, the logger name, the level, the stack, or any value.
  • A subprocess meter counts spawns; it never carries the command — We receive how many subprocesses were spawned per minute and the total for the window — never the command line, its arguments, its path, its environment, or its output. Thread, worker, handle, timer, module and descriptor meters work the same way: only how many, never their names.
  • On a phone: the platform and version band it runs on — From the React Native, Flutter, Swift/iOS and Android/Kotlin kits only: the operating system word — ios, ipados or android — and one version number, either the iOS major version or the Android API level. Both are read from the operating system's own version report. It is here because a platform rule that changed between two OS releases, filed under one name, is a wrong answer rather than a vague one.
  • On an Android phone: the maker, from a short fixed list — Only makers whose own battery management genuinely changes whether a background job runs at all — Samsung, Xiaomi, Google, Sony and a handful of others. A maker not on the list is dropped rather than sent. Never the model, the device name, a device identifier, the OS build string, the carrier, the locale, the screen, the battery, or anything else about the phone.
  • On a phone: what the operating system let the app do — Up to seven yes/no observations of things the kit watched happen: whether work already in flight finished while the app was in the background, whether a timer already scheduled fired while it was away, whether the system warned the app before it ran short of memory, whether the app could read its own memory ceiling and its own CPU clock, and, on Apple platforms, whether the system will say how much background time is left and whether that number counts down. Each is a 1 or a 0, a fact the kit could not observe is left out rather than sent as a 0, and no measurement is taken to answer them — each is a by-product of work the kit was doing anyway. They are pooled per platform, no line is shown until at least three separate installs have reported it, and nothing in the pooled record can be traced to a particular phone.
  • What every traced step carries — Where a kit traces an action — a request your server handles, a screen of a React Native app, a page's call to its own server — each step it sends travels on these same two switches and carries, beside its route label, when it started after the action began, how long it took and a rating bucket for that time: a trace ID of 32 random hexadecimal characters tying together the steps of one action, minted in your app or taken from the request another of your instrumented services sent; a span ID, a shorter value of 16 random hexadecimal characters naming the step, and, where the step has one, the span ID of its parent — a step the kit made itself, or the span ID your app's caller put on its request; and, where the kit reports them, the kind of work as one word from a fixed list of eight (handler, db, http, cache, queue, job, render or other) and whether it ended ok or in error. A step with no outcome is recorded as not reported, never as ok. None of these is derived from a person, the device, the account or the install. Boosthis keeps traced steps for 7 days, at most 20 for each trace. The Flutter kit's traced call, which rides full details alone, is described in its own group below.

How to tell: Two things to check, not one: search your own source for enableTelemetry (or enable_telemetry), and open the project in your Boosthis dashboard to see whether meter sharing is on. Either one on means the snapshot is being sent; only with both off is it not.

If your project runs a server kit: the operating system it runs on

Depends on you Decided by which kit you installed, not by a setting. The server kits send it — Node, Bun, Python, Java, Go, PHP, .NET, Ruby, Rust and Elixir — from the kit release that first reports it, each time the install registers, whatever else is switched on. The phone kits send their own system word inside the meter snapshot (above), and the browser and edge kits leave it out. Erasing the install, and BOOSTHIS_DISABLED=1, each stop it with everything else.

  • On a server: the operating system family — One word from a fixed list — linux, macos, windows, or other for anything else — sent with where a server runs each time the install registers, and read from the language runtime's own report of the system it runs on. A word not on the list is dropped rather than kept. It is a family: not a version, a build number, a machine name, a user name or anything else about the computer.

How to tell: Check which kit your project uses: a server kit reports it, and a phone, browser or edge kit does not. Then open the project's performance page in your Boosthis dashboard: beside where the install runs, an "Operating system" line names the system, or says the kit did not report one.

If the route list is on (it rides the meter snapshot's switches)

Depends on you It travels only when the meter snapshot does — your code calling enableTelemetry(), or meter sharing switched on for that project — and it has a switch of its own on top of those: BOOSTHIS_ROUTE_LIST=0 on Node and Python, or setRouteListEnabled(false) in the browser and React Native kits. disableTelemetry(), erasing the install, and BOOSTHIS_DISABLED=1 each stop it with everything else.

  • The list of routes your app has — The kit asks your own running framework for its route table — Express, Fastify, Koa and Hapi; FastAPI, Starlette, Flask and Django; Vue Router, React Router or Angular Router if you hand the kit your router object; React Navigation on React Native if you hand the kit your navigator's screen configuration — and sends the route names it answers with, including routes no traffic has ever reached. It is a question put to a running framework through that framework's own interface: we never read your source code, never open a file of yours, and never crawl or click through your app to find pages. You can also hand the list over yourself, and a hand-written list and a framework-read one merge rather than replace each other.
  • A route template, never a filled-in path — Each entry is a code-defined route template and nothing else — GET /orders/:id, /admin/users/<int:user_id>, or a screen name such as Checkout. Parameters arrive as the template your framework holds, so a real order number or user id is never in it, and each entry records only whether it came from the framework, from a list you declared by hand, or both. Never a query string, a handler name, a file path, source, or anything a request carried.
  • A route name that fails the guard is dropped, not redacted — Every name goes through the same guard that governs route labels on performance samples. A name that fails it is dropped entirely rather than partly redacted — a redacted route would otherwise draw on your map as a page of your app that does not exist.
  • Nothing rides on a route nobody visited — No timings, no counts, no scores and no ratings travel for a route the kit has not measured: an unvisited route carries a name and nothing else. On your map it is shown as not seen — never as dead, unused or broken. We cannot know why a route is quiet, so we do not say.
  • At most 200 routes, and it says how many it found — At most 200 routes travel. The list also states how many the kit found, so a larger app reads as at least this many rather than as a complete picture. Where a framework has no way to answer, nothing at all is sent for it and your map says it shows the pages traffic reached, rather than implying your app has no others.

How to tell: Two things decide it. First, is the meter snapshot being sent at all — search your source for enableTelemetry (or enable_telemetry), and check meter sharing for that project in your Boosthis dashboard. Then, if it is, open the project's map page: it names where each route came from, and says plainly when the list is switched off or when the kit could not ask your framework.

If the control census is on (it rides the meter snapshot's switches)

Depends on you It travels only when the meter snapshot does — your code calling enableTelemetry(), or meter sharing switched on for that project — and it has a switch of its own on top of those: setControlCensusEnabled(false) in the browser and React Native kits, or BOOSTHIS_CONTROL_CENSUS=0. disableTelemetry(), erasing the install, and BOOSTHIS_DISABLED=1 each stop it with everything else.

  • How many controls a page has, and how many were never pressed — The browser kit asks the page it is already inside how many interactive elements it has — the same question the route list puts to your framework, one level down. It is the running interface describing its own shape: we never read your source code, never open a file of yours, and never crawl, click or drive anything to discover controls. What travels is a count, and one entry per control.
  • A position in the page, never a label, a name or a value — Each control is identified by a hash of its POSITION in the rendered page and nothing else. Not its text, not its label, not its accessibility name, not its value or placeholder, not its id or class, not anything a person typed — none of it is read, and the identity that travels is a short opaque string no name can produce. Boosthis has a count and a set of positions, and no name for any of your controls. Alongside the position, each entry carries only what KIND of control it is, from a fixed list of five words, whether it has been pressed, and whether pressing it opened another page.
  • A destination is a route name you already send — When pressing a control is followed by a page change, the entry records the page it went to — a route name of exactly the kind your app already sends, through exactly the same guard, dropped entirely rather than redacted if it fails. A control that was pressed and left you on the same page records that it stayed, which is a fact about the control rather than a fault with it.
  • Which control was pressed and did nothing — The browser kit has always counted how many presses on a page did nothing at all — no navigation, no change, no scroll. That count is unchanged. What is added is which control it was: alongside each control's position, the entry carries a count of presses that changed something, presses that changed nothing, and presses the kit could not judge either way. A control that changed something is reported as having RESPONDED, never as correct: Boosthis sees that something changed, never whether it was the right thing. Nothing about the press itself travels — no coordinate, no time of day, no sequence, nothing about the person who made it — and nothing about the control beyond the position you already read about above.
  • At most 300 controls, and it says when it stopped early — At most 300 controls travel individually. The scan also stops if a page is very large or if it reaches its own few-millisecond budget, and when it does the report says so, so the count reads as at least this many rather than as the whole page. A control behind a closed menu or an unopened dialog is not rendered, so it is not counted.
  • Where it cannot be done at all — Only a kit inside a rendered interface can ask. The React Native kit sends the block and says in it that it can see a control was pressed but not which one, because its touch observation carries a timestamp and nothing else — so it also reports no dead presses against any control, rather than a number you could not act on. Every server-side kit — Node, Python, Java, Go, PHP, .NET, Ruby, Rust, Elixir and edge — renders nothing, so there is no interface to ask and nothing at all is sent; your map says so in words for that part of your app rather than showing a page with no controls on it.

How to tell: Only the browser and React Native kits answer this question at all; no server-side kit has a rendered interface to ask. Open the project's map page: it states the control count for the page the latest report was taken on, and says plainly when the census is switched off, when the kit could not read the page, and when a runtime has no interface to ask.

If you switched a project into detailed crash mode

Depends on you Off unless you turn it on for a specific project from your Boosthis dashboard.

  • The first line of an error message — Only the first line, cut to at most 300 characters, and only if it passes our guard — if anything in it looks identifier-shaped (an email, token, IP, phone number), the entire line is dropped rather than partly redacted. Whatever the line says is kept as written. If your error messages quote user input, this is the field it would reach.
  • Sanitized stack frames — Up to 20 frames, each reduced to a function name, a file basename and a line or column. The kit reads each frame's file path inside your process in order to cut it back; absolute paths, URLs, query strings, arguments and local variables are never included in what leaves it.

How to tell: Open the project in your Boosthis dashboard: detailed crash mode is a per-project setting shown there.

If your app is a React Native app

Depends on you Decided by which kit you installed, not by a setting. Only the React Native kit sends any of it. The daily reach tag rides the always-on signals. The other three are collectors the kit starts itself when your code calls enableTelemetry(), with no setting to pass, and what they measure travels only when the meter snapshot does — full performance details, or meter sharing switched on for that project. Each of those three can be refused by name: autoWrapNetwork: false, trackUnhandledErrors: false, or trackImageWeight: false. A refused reading wires nothing and its meter says it is switched off in this build. disableTelemetry(), erasing the install, and BOOSTHIS_DISABLED=1 each stop all of it with everything else.

  • A daily-rotating reach tag — A small random value the device mints fresh each calendar day, never derived from the device, the user, an account or the install ID. We fold it into a coarse estimate of roughly how many distinct devices hit the same issue this week and never store the tag itself. Because it rotates daily it cannot follow a device across days.
  • How long this app's own requests took, and how they ended — The kit observes the app's own global fetch and XMLHttpRequest and records two values per attempt: a duration in milliseconds, and one word for how it finished — ok, error, timeout or near-hang. There is no field anywhere in that path for a URL, a host, a path, a query string, a header, a request or response body, or a status code: none of them is read at all, rather than read and dropped later. Boosthis's own uploads are excluded, so an app making no requests of its own reports nothing here.
  • A count of unhandled errors and unhandled promise rejections — How many of each happened, and nothing whatever about any of them: never the error, never its message, never its stack, never its type. The kit chains the app's existing handler rather than replacing it, so an existing crash reporter and React Native's red box behave exactly as they did before.
  • How much bigger an image was than the space it filled — For images the app renders with React Native's own Image component, the kit reads four numbers as each one loads and lays out: the decoded width and height, and the displayed width and height. Never the image, never its URL or file name, never anything drawn in it.

How to tell: It applies if you installed the React Native kit in a mobile app; no other runtime sends it. The three the kit starts itself are on unless your own enableTelemetry() call refuses them, so read the options you pass it — search your source for enableTelemetry. The kit's own panel and the project's meters then say, for each of the three, whether it is measuring or switched off in this build.

If the React Native screen circuit is on, and full details or meter sharing is on

Depends on you On by default from React Native kit 1.0.0-alpha.297, and off with traceScreens: false in enableTelemetry(); an older kit records nothing unless your code passes traceScreens. It records only while one of the meter snapshot's two switches is on — your code calling enableTelemetry(), or meter sharing on the project — and never while your app has recorded that its user withheld permission (setEndUserConsent("withheld")); until then it reads no request's address and makes no screen span, call span or trace header. disableTelemetry(), erasing the install and BOOSTHIS_DISABLED=1 each stop it. The separate Network meter is unchanged and remains blind — it still reads no URL, host, path or status.

  • The route names of traced phone screens — For each screen the app moves to, the kit reads the route name written in the navigator and how long the screen took. It never reads screen params, titles or anything a person typed.
  • The calls made from traced phone screens — For calls made from that screen, the kit sends the method, an id-redacted path, how long the call took and whether it failed. It never sends the query string, body, headers, response status number or destination host. A trace header is written only to a host the developer named.

How to tell: Check the React Native kit's version and search the app's enableTelemetry() call for traceScreens. On 1.0.0-alpha.297 or later without traceScreens: false, the circuit is recorded whenever one of the meter snapshot's two switches is on; on an older kit only an explicit traceScreens records it. A propagateTo list names the only hosts that may receive the trace header; with no named host, no host receives it.

If your web pages call your own server (browser kit 1.0.0-alpha.144 and later)

Depends on you On by default from browser kit 1.0.0-alpha.144, and off with traceSameOrigin: false in the kit's options. Each fetch or XHR request a page makes to its own origin then carries three trace headers to your server and is recorded as one traced step. A request to any other host carries no trace header unless your code names that host in tracePropagateTo. The step travels on the same two switches as the meter snapshot — your code calling enableTelemetry(), or meter sharing on the project — and disableTelemetry(), erasing the install and BOOSTHIS_DISABLED=1 each stop it.

  • Your pages' requests to your own server — For each request a page makes to its own origin, the kit sends the page's route label, how long the request took and whether it failed, and passes your own server three trace headers: a random trace ID, a random step ID and an elapsed time of zero, none of them derived from the visitor, the device, the account or the install. It never sends the request's address, query string, body, headers or response status number.

How to tell: Check the browser kit's version and search your source for traceSameOrigin: false. On 1.0.0-alpha.144 or later without that line, a page's requests to its own origin are traced whenever one of the meter snapshot's two switches is on. tracePropagateTo lists the only other hosts that receive the trace headers; with no host named, no other host receives them.

If your server runs a Boosthis server kit (Node 1.0.0-alpha.200, Python 1.0.0a168, PHP 1.0.83, Java and Go 1.0.0a146, Ruby 1.0.0a86, .NET 1.0.89, Rust 1.0.0a81, Elixir 1.0.0a78, edge 1.0.0-alpha.32 and later)

Depends on you Decided by which kit your server runs, and its release: the Node kit from 1.0.0-alpha.200, the Python kit from 1.0.0a168, the PHP kit from 1.0.83, the Java and Go kits from 1.0.0a146, the Ruby kit from 1.0.0a86, the .NET kit from 1.0.89, the Rust kit from 1.0.0a81, the Elixir kit from 1.0.0a78 and the edge kit from 1.0.0-alpha.32 send these, and no browser or phone kit does. They travel only together with the request's own traced step, on the same two switches as the meter snapshot — your code calling enableTelemetry(), or meter sharing on the project — and disableTelemetry(), erasing the install and BOOSTHIS_DISABLED=1 each stop them.

  • Which kinds of database and outside-service calls a request made — For each request it traces (and, on the Node, Python, Java and Go kits, each background-job run), the kit sends one linked step for each kind of call made inside it — database, or an outside service grouped as signin, storage, payments, messaging, search or other — with when the first such call started, how long calls of that kind were in flight, a rating bucket for that time and whether any of them failed. Repeated calls of one kind are folded into one step, seven at most per request, and a call to an AI provider is never linked. It never sends the statement or query, a table name, the service's host or address, a path, a query string, a body, headers, a response status number or any value.

How to tell: Check which kit your server runs and its version. On one of the releases above or later, a traced request also sends one linked step for each kind of database or outside-service call it made, whenever one of the meter snapshot's two switches is on.

If your server runs the Node or Python kit and calls your own services (on by default)

Depends on you On by default in the Node and Python kits, and changed by one setting on your server, BOOSTHIS_TRACE_PROPAGATION: none turns it off, internal keeps internal addresses only, and a list of hosts replaces the default. With nothing set, the kit adds three trace headers to its calls to addresses that cannot be on the public internet and, from Node 1.0.0-alpha.204 and Python 1.0.0a171, to hosts on the same site as the request being served. No other company's own domain receives them unless you name it, nothing new is sent to Boosthis, and BOOSTHIS_DISABLED=1 stops it too.

  • Trace headers on your server's calls to your own services — When your server calls another of your own services while it handles a traced request, the kit adds three headers to that call: the request's trace ID, the milliseconds since that trace began and a random ID for the call, none of them derived from a person, the device, the account or the install. With nothing set they go only to addresses that cannot be on the public internet and to hosts on the same site as the request being served: the same registrable domain under the public-suffix list, so two apps that share only a hosting address, such as two apps on replit.app, are two sites. A name on your own domain that you have pointed at another company's service is on your site by that rule. A payment provider, a model vendor or any other company's own domain receives none of them unless you name it.

How to tell: Check which kit your server runs and its version, and whether BOOSTHIS_TRACE_PROPAGATION is set where it runs. The kit's own status page states the rule in force in one line.

If your app is a Flutter app

Depends on you Decided by which kit you installed, not by a setting: only the Flutter kit does this. It watches the requests your app sends through Dart's own HTTP client, which covers package:http and Dio on its default adapter, and the optional boosthis_dio add-on watches every call a Dio instance makes once your code adds its interceptor. While the kit is not switched off, each such request carries three trace headers to whatever host it goes to, and only BOOSTHIS_DISABLED, set when the app is built, stops them. The traced step for each call is sent only while full details are on for the project, and forget stops anything being sent. In a Flutter app running in a browser, the kit and the add-on stand down: nothing is measured and no header is added.

  • How long your app's outgoing calls took — Each call your app makes through Dart's own HTTP client, or through a Dio instance carrying the add-on's interceptor, is counted once, whichever of the two sees it: how long it took and whether it stalled, folded into the network reading, and, where the host is one the kit recognises as an AI provider, the AI-call readings. The host and the path are read inside your app only for that check, and neither is sent. Only while full details are on, the call is also sent as one traced step labelled with the request's method and the fixed word /outbound (for example GET /outbound), with how long it took, its place after the trace began, its trace ID, its own step ID and the step it came from, the kind http, and whether it ended ok or in error. Never its address, host, path, query string, headers, body or response status number.
  • Trace headers on every outgoing request, to any host — While the kit is not switched off, each request it watches carries three trace headers to whatever host it goes to: a random trace ID, the call's random step ID and the milliseconds since the trace began, in place of any of ours the app set itself. None of them is derived from a person, the device, the account or the install. Unlike the browser and React Native kits, the Flutter kit has no list of hosts that limits where they are written, so a request to another company's service carries them too. Removing the interceptor stops the add-on and turning full details off stops the traced step, but only BOOSTHIS_DISABLED, set when the app is built, stops the headers: until then they are added even where nothing is being sent.

How to tell: It applies if your app runs the Flutter kit; no other kit writes trace headers to every host. Search your build configuration for BOOSTHIS_DISABLED: unless it is set when the app is built, every request through Dart's HTTP client carries the three headers, whatever host it goes to. Search your source for BoosthisDioInterceptor to see whether the add-on is in use, and open the project on your Boosthis dashboard to see whether full details are on.

If your app runs in visitors' web browsers (browser kit 1.0.0-alpha.146 and later)

Depends on you Decided by which kit you installed, not by a setting. Only the browser kit sends it — the hosted script tag and the browser module alike — from 1.0.0-alpha.146, and it rides the always-on signals: the uploads and the registration the kit already makes carry it, with no request added. Erasing the install (forget()) deletes the day's value from the browser, and a visitor who switches off the kit's storage on their device erases it with everything else the kit keeps there.

  • A daily visit tag from each visitor's browser — A small random value each visitor's browser makes fresh every UTC day and sends as one header on the uploads and the registration the kit already makes, beside the install ID and the kit version. It is never derived from the visitor, the browser, the device, the page, an account or the install ID, and because it changes every UTC day it cannot link a browser across days. Boosthis folds it into estimates of roughly how many browsers sent readings that day, how many were refused and which kit versions they ran, and never stores or logs the tag itself. Where the browser lets the kit keep its storage the day's value is kept there; where it does not, each page load makes its own, counted apart and never added to the browser count.

How to tell: It applies if your site runs the browser kit at 1.0.0-alpha.146 or later; no other runtime sends it. A site loading the hosted script tag runs the release Boosthis serves at that moment, and a site bundling the browser module runs the version its lockfile names; your project's page on Boosthis shows the kit versions your visitors' browsers reported.

The name your app registers under, and the name you type for the project

Depends on you An app sends a name only if your own code or build gives it one: the field is optional, and an install that sends none is listed by its identifier instead. The project name is not sent by anything — you type it on your Boosthis dashboard, and no kit ever sees it.

  • The name itself, as free text — A name is held as written, apart from surrounding spaces being dropped and a 60-character clamp — nothing else normalises, rewrites or shortens it, because a rewritten name would make your dashboard, your alerts and our AI answers disagree with one another. Both are free-text fields, so a name containing a person's name is held as written, and you should choose them as you would any label you publish internally. Two things do happen to one on the way out: a name carrying a value rather than a label — an address, a phone number, a token or a long numeric id — is withheld from answers rather than repeated back, and a registration whose name trips the same identifier guard that screens telemetry is refused at the door rather than stored.

How to tell: Both are on your dashboard: the app's name on the install's own row, the project's name at the top of the project. Search your own source or build configuration for the name you pass to the kit; if you pass none, none is sent. Renaming a project renames it for every key in its rotation line, so an app still shipping an older key reports under the new name from its next handshake.

If a developer uploads a build map so crash frames read as source

Depends on you Nothing is uploaded unless a developer does it — by hand from the project page, or by a step added to your own build. No kit fetches a build map's contents, and Boosthis never asks your build system for one; a project that has uploaded none holds none. One kit asks a question about a map rather than for its contents: the browser kit's development-posture check makes one HEAD request to your own site for the first script's .map address and keeps only whether the answer was OK, as a 1 or a 0. Nothing is downloaded, and the address is neither stored nor sent. Upload one and your crash frames resolve to your own file and line; upload none and they read as the compiled position, which is the only difference.

  • Your build's original source paths and identifier names — A build map is parsed the moment it arrives and only the parts needed to place a frame survive: the original source paths that build recorded (or, for an Android mapping file, the original class names), the original identifier and method names, and the table that joins them to positions in the compiled output. The uploaded file itself is not kept, and the embedded copy of your actual source — a source map's sourcesContent — is discarded at the door and never written down. What is kept is held for 90 days from the upload and is handed back only to you: your own crash frames resolved to your file, line and original identifier name, and your account export's list of the maps held with their source paths and expiry dates. The uploaded file is never served back to anyone, and the mapping table and the table of identifier names are never handed over as tables.

How to tell: The project page lists every build map the project holds: which release, which generated file, when it arrived and the date it ages out. An empty list means none is held right now — either none was ever uploaded, or the ones that were have aged out. Each map is kept for 90 days from the day it arrived and is then deleted; re-uploading the same release replaces the map already held rather than adding a second, and restarts that clock.

What it never sends

None of these leave your application, under any setting. A denylist enforces it on the device and again on our server, and a batch that trips either one is rejected whole.

  • IP addresses — Never as part of telemetry, and never used to identify anyone or joined to telemetry. Like nearly every web service our server keeps operational request logs. In place of the address, each line carries a tag that changes every day and cannot be turned back into it, and the logs are deleted after 30 days, older lines that still held the full address included. Rate limits count requests per project key or install, never per address.
  • Anything that identifies one of your users — User IDs, email addresses, names, phone numbers, postal addresses, device IDs or any other identifier. A denylist blocks them on the device and again on our server, and a batch that trips it is rejected whole.
  • The contents of requests and responses — Request bodies, response bodies, query strings and headers are never collected and never sent. Reading something inside your process and collecting it are two different things, and this is the whole of the first. The one exception is the AI-call meter: to tell repeated prompts apart it reads up to the first 4,096 characters of an outbound request body to an AI provider, inside your own process, and reduces it immediately to a single number — no body text is stored, uploaded or recoverable. Nine other bounded reads work the same way — inside your own process, reduced on the spot to a number, a flag or a label from a fixed list: the same meter reads the provider's reply as it streams back, holding at most 8,192 characters of a partial line at a time, to take the token counts the provider states in it; when a provider refuses a call, the meter takes its own copy of that error reply to say which kind of refusal it was — the Python kit stops accumulating at 8,192 characters, and in Node and the browser the runtime hands the reply over as one string, of which anything longer than 8,192 characters is dropped without being examined; the leak watch reads up to the first 8,192 bytes of an ERROR response your app is about to return, to see whether a stack trace or a secret-shaped string is about to reach one of your users; in the browser, and only if you switch it on, a copy of a JSON answer to one of your app's own calls is read to see whether it is error-shaped despite its 200: the copy is taken only where the server declared a length of 65,536 bytes or less, and because a declared length can understate what arrives, the copy the browser hands back is checked again and dropped unexamined past 65,536 characters; on an MCP endpoint your app serves, up to the first 64 KB of the JSON-RPC request is mirrored as your app reads it, to take the method name out of the envelope — never the arguments, and never a byte of what the app itself receives is changed; in the edge kit, the repeated-work and database readings fold up to 4,096 characters at each end of an outgoing call's query, and of its body when the body is text, into one number, so that two different calls to the same address are never counted as the same work; the number is compared inside the request and dropped with it; the cookie check reads the Set-Cookie headers your app sends — at most 20 per response, and at most 2,048 characters of the attribute tail on each, which is 2,048 bytes in the kits whose strings are bytes — for the Secure, HttpOnly and SameSite flags and the size, never the value; the Node kit also reads each cookie's name, up to 1,024 characters and stopping where the name ends, and matches it against a fixed word list only to decide whether a missing HttpOnly matters — a session or token cookie needs it, a CSRF or preference cookie is meant to be read by your page's own script — and the name is never kept or sent; the Cookie header your users send is not read at all; a fixed list of headers is looked up by name and turned straight into a number or a fixed label: the content type, length and encoding, the Accept header your user's browser sent, whether your reply is marked as a download (Content-Disposition), the cache instructions and validators on your app's own reply (Cache-Control, ETag, Last-Modified), the elapsed-milliseconds offset a previous instrumented hop wrote, the queue-start timestamp a proxy adds, the presence (never the value) of a reverse proxy's forwarding headers (X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, X-Real-IP), whether the Host and Origin headers on a request for one of the kit's local-only answers name this machine (a yes or no that decides whether the kit answers; neither value is kept), whether a request carried an Origin header and whether the cross-origin headers on your app's own reply (Access-Control-Allow-Origin and Access-Control-Allow-Credentials) answer any origin, repeat that Origin back, or pair any origin with credentials (yes-or-no answers that become counts; no origin or header value is kept), a hosting platform's cache verdict, an AI provider's rate-limit headroom, retry delay and service-side processing time, and whether an authorization header or an authentication challenge is present — never its value, and never a header nobody named: the list is the claim, and a kit that starts reading a header outside it fails our own build before it ships; and the query values the kit reads on its way OUT are the ones in URLs the kit itself builds for its own uploads, read key by key so the guard can refuse anything identifier-shaped before it leaves your process; your app's own query strings are cut off, unread, before a route label is made. One bounded read is not a reduction at all, because what it holds is your own page on its way to your own visitor: where the dashboard bubble is switched on, an HTML page is buffered so the one script tag can be written into it — up to 2 MB, 4 MB on .NET, past which the page is passed through untouched. Those bytes are handed back with one script tag added and nothing taken from them. One read is neither narrowed to a label from a fixed list nor limited in length, because the value IS the question being asked of the kit's own endpoint: the kit's own local read endpoints — the panel and the JSON reads it serves under its own path, behind their loopback or key gate — read the query of requests made TO THEM: how many rows to return, and which route name to filter to; the row count becomes a number, the route name is compared, exactly as the caller wrote it and with no limit on its length, against the route names the kit is already holding, so a name matching none of them simply returns nothing; neither value is recorded, and these endpoints read no other query string in your app. Nothing checks it against a list first, no cap is put on how long it may be, and nothing is derived from it or kept once the reply has gone. One bounded read does not stay inside your process, and the difference is set out here rather than left to be inferred: on a request arriving at your app, the kits on your servers read two headers for their VALUE rather than for a label — the trace id and the caller's span id that another Boosthis-instrumented service of yours wrote on it. A value is taken only if it is exactly the shape the kits mint (32 hexadecimal characters for the trace, 16 for the span), and anything else is discarded, the trace id replaced with a fresh random one. An adopted trace id then IS that request's trace id: it is put on the hops your app makes while handling the request, and it is uploaded with that request's timings; an adopted span id is uploaded as the parent of that request's own step. That is the only way one action crossing several of your services can be shown as one line rather than several unrelated ones. Those two ids are the only things read here that are carried onward or uploaded with your app's readings; each is an identifier the kits mint and nothing is derived from it, and no other header's value is read. Two reads send what they hold to Boosthis, because what is held is a credential Boosthis itself issued and only Boosthis can say whether it still stands: where the dashboard bubble is switched on, the kit looks in a page's address for one key, boosthis — a server kit in the address of each page request your app receives, the browser kit in the page's own address — and only for one of three answers: a one-time owner proof from your project's page on Boosthis, which is sent to Boosthis to be swapped for an owner pass that lasts about 30 days (a server kit keeps it in a cookie of its own, the browser kit in that browser's own storage); off, which forgets that pass; or on, which only a server kit honours, and only from the machine itself; when the kit acts on one, the browser is left at the same address without that key, no other key in the address is used or kept, and the switch-off control in a server kit's bubble only ever writes that key, as off, into the address of the page it is on; and a server kit looks in the cookie header of each request your app receives for two cookies of its own and uses no other: boosthis_owner_pass, which holds the owner pass Boosthis issued through an owner link, and boosthis_owner, which holds only 1, marking the browser as switched on; the pass is sent to Boosthis to ask whether it still stands, and the kit keeps the answer for up to ten minutes (thirty seconds when Boosthis did not answer); a request without that cookie makes no call at all; the bubble's script in the page reads the second cookie to decide whether to draw; where a kit can only set its cookies by rewriting the response's Set-Cookie lines, it takes the ones your app already put there and hands them back unchanged beside its own; and on a page that carries the bubble it adds Cookie to your app's Vary header, keeping what was there. One read is neither bounded nor reduced to a number, and you ask for it yourself: one endpoint the Python kit's panel serves takes a POST — the rule matcher — and it reads that request's body in full, because the code you paste in IS the question being asked: it is matched against the rule book inside your own process, behind the same loopback or key gate as the rest of the panel, the answer is a list of rule names, and neither the code nor any part of it is stored, logged or uploaded. Apart from the trace id and span id another instrumented service already put on the request, the owner link's one key, boosthis, in a page's address and the kit's own two owner cookies, none of what is read here is stored, uploaded or recoverable from what is uploaded, and the guard on the upload path refuses outright any payload carrying a request or response body, a header, a cookie or a query value — and those ids are uploaded as fields of their own, never as the header they arrived in, and the owner proof and pass go to Boosthis on calls of their own, never inside an upload.
  • Raw stack traces, raw error messages and log lines — A crash report carries a redacted code location and an error type, the screen or route it happened on (or a reason from a fixed list where none can be named) and, where it happened during a traced action, that action's trace references — and, only in detailed crash mode, a first line that passed the guard, cut to at most 300 characters, and up to 20 frames reduced the same way. Never a raw stack and never a value. A meter that watches logged errors records how many and how often, never the line, the message, the logger or the level.
  • Anything about the processes your app spawns — Command lines, arguments, environments and output are never sent. The subprocess meter records only how many spawns happened and how often.
  • The names of the things the meters count — Threads, workers, modules, handles, timers, files, pools and queues are counted, never named. A meter reports how many and how long, never which.
  • Your source code — Boosthis never reads your codebase. No kit opens, scans, indexes or uploads a source file, and no source text is stored on our servers — including from a build map, whose embedded copy of your source is discarded the moment the map arrives and is never written down. What we can say about a line of your code comes from what your own build already put in a stack frame.
  • Your hostnames and your environment — Hostnames, and the values of your environment variables, are never sent, with two exceptions. The first is the region code a hosting platform publishes about itself, such as us-east-1, sent with where a server runs (above). The second is an outside service your app calls in a burst: where a kit watches your app's outgoing calls for one host being called again and again without backing off — five or more calls to it within ten seconds — it reports that as a problem named after the host alone, such as api.example.com, never a path, query string, port or anything else from the address. The Node kit watches for this on its own, and some other kits only where your code hands them each outgoing call. A kit does read environment variables inside your own process — its own settings, and the markers a hosting platform publishes about itself — and, apart from that region code, what leaves is a word from a fixed list or a number worked out there, never the value it read. The browser kit's development-posture check, for one, reads NODE_ENV to report whether a development build is running, as a 1 or a 0; and the browser kit reads NODE_ENV and the page's own hostname to recognise a developer's own page — a development build, or a loopback address such as localhost — and when it is one, sends only the word "development" and reports that page as the project's separate development install. File paths are never sent from your machine either — with one exception, named here rather than further down the page: a build map a developer uploads keeps its own original source paths, for as long as that map is kept. Two things read paths without sending them: a kit reads the paths in an error's stack inside your process only to cut each one back to its file name, so a crash frame's file is a basename; and the Boosthis editor extension's chat reader opens the conversation files a developer's AI assistant keeps on their own disk.
  • Anything your users see or say — Screen contents, screenshots, video and audio are never captured.

What it stores on your users' devices

Everything above answers “what do you send Boosthis?”. This answers the other question a reviewer asks about a third party inside your app: what does it put on my users' machines? It applies to the browser kit — on a server or a phone the kit's state lives on the machine you run, not on a user's device.

  • A few named entries in the browser's own storage — In a browser, the kit keeps a small amount of state in the site's own local storage: the install's identifier, the project it reports to, the token that lets that application read its own measurements back, the notice version the user agreed to where the application shows one; measurements taken but not yet uploaded — a crash waiting to be sent, which is retried and dropped after 30 days, a page-view tally, the page map and the baselines a candidate rule is compared against; a short crash history — for each time the site was opened in the last 24 hours, at most 32 of them, when it began and was last in use, the times of its crashes and how many errors from browser extensions were set aside, times and counts only, each dropped after 24 hours; the day's visit tag, replaced each day, and when the current visit began; where Boosthis has asked the kit to hold back its uploads for a while, the time until which it should; where someone signs in to a Boosthis account from the kit's own panel, that sign-in and the email address cut down to its first letter and its domain, such as f…@example.com; the cached answer to whether the install is still switched on; and — where the site's owner opened it with the owner link from their project's page on Boosthis — the owner pass Boosthis gave that browser and the time it runs out. Where a server kit's dashboard bubble is switched on, the kit sets two cookies in that one browser, on the application's own domain (boosthis_owner_pass, holding the confirmed owner pass and unreadable by the page's scripts, and boosthis_owner, set to 1), and the bubble's script keeps one entry there: the version of the notice accepted inside the bubble. No other browser is given either. None of it is a profile and none of it is shared — it is what one install needs in order to keep working across reloads.
  • In a phone app, the same kind of state in the app's private storage — In a phone app, the React Native, Flutter, Swift and Kotlin kits keep the same kind of state in that app's own private storage, which no other app can read, and set no cookie: the install's identifier, the project it reports to, the app's name and the credentials Boosthis issued to the install; whether the install has registered, the answer about sharing that Boosthis last gave it, and when it was switched on; measurements taken but not yet uploaded — a crash waiting to be sent is kept, tried again at each later launch and dropped after 30 days — with the time of the last upload, the screen map and the baselines a candidate rule is compared against; the cached answer to whether the install is still switched on; on the React Native kit, the day's reach tag, replaced each day; and, where your code hands a Flutter, Swift or Kotlin kit its outgoing calls to watch for bursts, the host names called in the last ten seconds and when, at most 200 of them. Removing the app removes all of it, and so does the kit's own forget(). On both the browser and the phone kits, an end user's consent switch set to withheld or to this session only erases it too. What a phone already uploaded stays with the app's install, which every phone running that build shares, until the app's owner erases it.
  • No cookie, and nothing another site can read — The kit sets no cookie in the application it runs in, and nothing it writes is readable by any other site. Every entry's name begins with boosthis: — so it never collides with, and never enumerates, the application's own storage.
  • Where the browser refuses storage, it uses memory that dies with the page — Private mode, a sandboxed frame or storage switched off: the kit falls back to memory that dies with the page rather than failing. Clearing the site's data removes all of it, and so does the kit's own forget(), which also stops that browser sending; what it already uploaded stays with the site's install, which all of its visitors' browsers share, until the site's owner erases it.

Where it is processed

Readings from your app are held on our infrastructure, which runs outside the Kingdom of Saudi Arabia:

  • Replit — hosting and the managed PostgreSQL database, and the AI gateway in front of OpenAI. Runs in the United States.
  • Google Cloud — backup storage — the routine encrypted database backups described under Retention. Runs in the United States.

These are the companies that hold what the kit sends. The full list of every company that can see any of your data — including the payment and email providers, which never receive telemetry — is in our Privacy Policy.

How long it is kept

  • Performance samples — Each performance sample is kept for 30 days from when it arrives, and routine housekeeping then deletes it. Erasing the install deletes samples immediately, and they are deleted after the 60-day countdown that follows a revoked or suspended key.
  • Traced steps — At most 20 steps are kept for any one trace, and each is deleted once it is older than 7 days. Routine housekeeping deletes steps past that age, a batch at a time and oldest first, and so does a share of the uploads that bring new traced steps, so a step can outlive 7 days by as long as a backlog takes to clear. The daily record of how each traced action was wired — how many routes, runtimes and layers it touched, never the steps themselves — is kept for 90 days, and only that share of uploads deletes it, so it can outlive 90 days: longer when few steps are arriving, and with no limit while none arrive at all. Erasing the install or closing the account deletes both.
  • Meter snapshots — Only the latest snapshot per project is kept — each upload replaces the previous one.
  • Issue, fix and crash signals — One row per install and signature, and repeats just add to a counter. An issue signature is deleted 365 days after it was last reported and a crash signature 365 days after the crash was last reported — either stays longer while it is still the evidence behind a rule, or behind a proposed one — and a fix signal is deleted 365 days after it was last reported. If you erase the install they are not deleted but permanently de-identified: the install identifier is replaced with a random anonymous key, and any detailed crash fields are removed, so what remains cannot be traced back to your app. That remainder is deleted 180 days after it was last reported.
  • The register of your app's parts — The route and screen names your project has been observed to have, pooled across your installs so that replacing an install does not restart a part's history. It is built only from readings that arrived — never from a route list your framework or navigator declares — and each entry is kept for 180 days after that part was last seen, so a part that has gone quiet stays on the list, marked quiet, with the date. Erasing your project's last install deletes the register, and so does closing your account.
  • Which kits your project needs — Which Boosthis kits your project was worked out to need — the kit names only, from our own published list, with when and under which version of the scanner's rules they were worked out. It arrives when you ask the Boosthis connection which kits this project needs, or when you deliberately send the hosted scanner's answer, and it is kept for 365 days after it was last given so your project row can show the languages that are not reporting yet. To work the answer out, the connection accepts only names on Boosthis's published signal list — file names such as package.json and framework names such as express — plus folder labels your assistant chooses; anything else it is sent is dropped on arrival, unused and unkept, and its answer says how many items were dropped. No manifest contents, no dependency names outside that list and no source code are asked for, and nothing from your project is stored but the kit names. Closing your account deletes it, and so does erasing the project.
  • Backups — Routine backups of our operational database are kept for up to 30 days and rotate out automatically, so erased data disappears from them as they rotate. Two narrow exceptions: a single backup may be held past that window while it is the last evidence in an open question about our own records, and a small separate copy of the payment and receipt records we are required by law to keep does not rotate out with the rest.

How one of your users can be forgotten

Your users' data is not the data we hold — and that is the honest answer to give a reviewer who asks. The kit sends nothing that identifies a person: no user IDs, no IP addresses, no request contents, nothing from a screen. So there is no per-user record here to find, export or delete, because nothing we store is keyed to one of your users in the first place.

What exists is per install — your application, not your users. You can stop it or erase it yourself at any time, without asking us:

  • Stop the optional details — disableTelemetry() stops per-route samples and meter snapshots, and leaves the always-on signals running.
  • Stop everything, without a code change — Setting BOOSTHIS_DISABLED=1 in your app's environment silences the whole kit, always-on signals included. It wins over every other setting.
  • Erase everything we hold for that install — boosthis telemetry forget deletes the install row and every sample, snapshot, trace and alert tied to it, permanently de-identifies the learning signals, and deletes the local config file. It is immediate and irreversible. For a website's or phone app's install — shared by every visitor's browser, or every phone running the app — one copy's request erases nothing pooled, and the install is erased by its owner, as the privacy policy describes.

Erasing an install is immediate and irreversible. Signals already learned from are not deleted but permanently de-identified: the install identifier is replaced with a random key, so what remains cannot be traced back to your app.

Words you can paste into your privacy policy

A short block naming us as a provider, describing the data, and linking to our full disclosure. Paste the first box; then add each extra sentence only where its condition is true for your app.

Paragraph for your privacy policy

True for every app that has Boosthis installed and registered. Start here:

Performance and error monitoring (Boosthis). We use Boosthis, a developer tool operated by Veqtara Tech Company (Rabwa District, Al Noaim Street, Riyadh, Saudi Arabia), to measure how this application performs and to find errors in it. Boosthis software runs inside the application and sends it technical measurements about the application itself: a random installation identifier, made from nothing about any device or person, that Boosthis links to the developer's account; coarse buckets describing a recurring performance problem, with, from the Node and browser kits, the code-defined screen or route label where it was last seen; coarse buckets describing when one of those problems improves; the type of an error together with a redacted code location (function name, file name and line) and the screen or route it happened on; where a crash came from beyond its top frame: that no stack came, the part of the framework that caught it, the causing widget's file and line, the first frame in the app's own code, and a debug-build mark; a short-lived, non-readable crash-batch fingerprint used only to suppress duplicate retries; an anonymous note about a slow or buggy pattern Boosthis has no rule for yet; the Boosthis version and the runtime it is running in; for a server, the hosting platform it runs on, the region code that platform declares, and whether it is production, a preview or development; the version of your own application, read from the build it was shipped in; the version strings of the releases the application declares, with when each shipped and how it was declared; and the source-written name of each scheduled job, how long its runs took and whether they succeeded. It does not receive IP addresses; user IDs, email addresses, names, phone numbers, postal addresses or device IDs; request or response bodies, query strings or headers; raw stack traces, raw error messages or log lines; the command lines, arguments, environments or output of processes the application spawns; the names of the things its meters count — threads, workers, modules, files or queues; your source code; hostnames or the values of environment variables — apart from a hosting platform's region code and the bare host of an outside service called in a burst — and, apart from the original source paths inside a build map a developer chooses to upload, file paths; and screen contents, screenshots, video or audio. The measurements are processed and stored by Replit and Google Cloud in the United States. When the developer has asked for alerts, an alert email naming the affected installation and describing the problem found is delivered by Resend in the United States. Measurements are kept while the application keeps reporting, and we can erase everything Boosthis holds for this application at any time. Boosthis's own statement of what it collects is at https://www.boosthis.com/docs/what-boosthis-collects.
Add only if: full performance details are on (a new project starts with them on)

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

This application also sends Boosthis the code-defined name of the route or function being measured, how long it took in milliseconds, and a good / needs-work / poor rating.
Add only if: the page map is on, and full details or meter sharing is on

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

In the browser and React Native kits, this application also sends Boosthis the route labels of the screens the application moves between, and which of them leads to which.
Add only if: full performance details OR meter sharing is switched on

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

This application also sends Boosthis the numbers behind its health meters: a score out of 100 and a good / needs-work / poor rating per meter, plus plain measurements — counts, timings, rates, percentages and sizes — with no text of any kind alongside those meter numbers. On a phone it additionally sends which phone operating system and version band it is running on, and yes/no observations of what the phone's operating system allowed — background work, timers, memory warnings — never anything about the handset or the person holding it. Each traced step it sends carries random identifiers that tie together the steps of one action and, where the kit reports them, the kind of work and whether it ended in error.
Add only if: your project runs a server kit: the operating system it runs on

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

On its servers, this application may also send Boosthis which family of operating system each one runs on — Linux, macOS, Windows or another — not a version or a machine name.
Add only if: the route list is on (it rides the meter snapshot's switches)

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

This application also sends Boosthis the list of routes your app has, read from your own running framework — including routes no traffic has reached. Each is a code-defined route template for each route, never a filled-in path, screened by the same guard as performance samples and dropped entirely rather than redacted if one fails it; nothing but the name travels for a route that has not been measured, up to at most 200 route names, with a count of how many were found.
Add only if: the control census is on (it rides the meter snapshot's switches)

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

This application also sends Boosthis a count of the controls on a rendered page, and which of them have never been pressed. Each control is identified by a hash of its position in the page for each control, never its text, label, name or value; where a control was seen to open another page, the route name a control was seen to open travels too, screened by the same guard as every other route name. It also sends a count of presses that did nothing, against the position of the control they were made on. At most 300 controls, with a statement of when the scan stopped early, and nothing at all from a runtime with no rendered interface, stated in words rather than sent as a zero.
Add only if: you switched a project into detailed crash mode

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

Crash reports from this application also include the first line of the error message (dropped entirely if it looks like it contains personal data) and stack frames reduced to a function name, file name and line.
Add only if: your app is a React Native app

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

On mobile, the application also sends a random tag that changes every day and is never derived from the device or any account, used only to estimate roughly how many devices hit the same problem. On mobile with full performance details or meter sharing switched on, it also sends how long this application's own network attempts took and one word for how each ended, with no URL, host, path, header, body or status code read at any point; a count of unhandled errors and unhandled promise rejections, with nothing about any of them; and four numbers for each image the application displays — its decoded width and height, and the width and height it was drawn at.
Add only if: the React Native screen circuit is on, and full details or meter sharing is on

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

If this application runs the React Native screen circuit with full performance details or meter sharing switched on, it also sends the navigator route name and duration of each screen it moves to, and the method, id-redacted path, duration and success or failure of calls made from that screen. It never sends screen params or titles, query strings, bodies, headers, response status numbers or destination hosts, and writes a trace header only to a host the developer named.
Add only if: your web pages call your own server (browser kit 1.0.0-alpha.144 and later)

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

If this application's web pages call its own server, it also sends the page's route label, duration and success or failure of each such request, and passes its own server three random trace headers. It never sends the request's address, query string, body, headers or response status number, and writes a trace header to another host only if the developer named that host.
Add only if: your server runs a Boosthis server kit (Node 1.0.0-alpha.200, Python 1.0.0a168, PHP 1.0.83, Java and Go 1.0.0a146, Ruby 1.0.0a86, .NET 1.0.89, Rust 1.0.0a81, Elixir 1.0.0a78, edge 1.0.0-alpha.32 and later)

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

If this application's server runs a Boosthis server kit, it also sends, for each traced request, one fixed word for each kind of database or outside-service call the request made, with its timing and whether it failed. It never sends the query, a table name, the service's host or address, a path, a body, headers, a response status number or any value.
Add only if: your server runs the Node or Python kit and calls your own services (on by default)

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

If this application's server runs the Node or Python kit, its calls to the application's own services, at internal addresses or on the same site as the request being served, also carry three trace headers (identifiers the kits generate and an elapsed time) so one action can be followed across them. No other company's own domain receives them unless the developer names it, and the developer can limit them to hosts they name or switch them off.
Add only if: your app is a Flutter app

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

If this application is a Flutter app, it also measures how long its outgoing calls took and whether they stalled, and, with full performance details switched on, sends one traced step per call labelled only with its method — never the address, host, path, query string, headers, body or response status number. Each outgoing request it watches carries three random trace headers to whatever host it goes to, unless the developer switched the kit off when the application was built.
Add only if: your app runs in visitors' web browsers (browser kit 1.0.0-alpha.146 and later)

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

In a web browser, the application also sends a random tag that changes every day and is never derived from the visitor's browser or any account, used only to estimate roughly how many visitors' browsers sent readings each day.
Add only if: The name your app registers under, and the name you type for the project

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

If the application sends a name it was built with, or a developer types a name for the project on their Boosthis dashboard, Boosthis stores that name as written, apart from surrounding spaces and a 60-character clamp. These are free-text fields chosen by developers rather than collected from users.
Add only if: a developer uploads a build map so crash frames read as source

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes data you are not sending:

If a developer working on this application uploads a build map so that crash reports read as source, Boosthis additionally holds that build's original source paths, its identifier names and the table joining them to the compiled output, for 90 days from the upload. The uploaded file is not kept and the copy of the source text embedded in it is discarded on arrival.
Add only if: your application runs in a web browser or is a mobile app

Add this sentence to the paragraph above only if this is true of your app — pasted otherwise, it describes storage your users never receive:

Where this application runs in a web browser or as a mobile app, Boosthis stores a small amount of state on the device itself — in a browser, in the browser's own local storage under entry names beginning with "boosthis:"; in a mobile app, in the app's own private storage — an installation identifier, the project it reports to, credentials the Boosthis server issued to this installation, and the software's own working state, including measurements taken but not yet sent. It sets no cookie, none of it is readable by any other website or app, and clearing this site's data or removing the app removes all of it.
Add only if: you let your users refuse or withdraw that storage, using the kit's end-user consent setting

Add this sentence after the one above only if this is true of your app — pasted otherwise, it promises your users a choice they do not have:

You can refuse or withdraw permission for this storage at any time. Boosthis then erases everything it stored on your device and either keeps measuring only in memory that is lost when the application closes, or stops measuring on your device altogether. What your device already sent stays with the rest of this application's measurements until we have them erased.

Adapt the wording to fit your own policy — the point is that the facts in it are ours to state and are accurate as they stand. If something here does not match what you see, write to support@boosthis.com and we will correct it.

If your reviewer wants more

  • Our full Privacy Policy — the same facts in our own terms, including every company that can see any of your data.
  • Before you install a Boosthis kit — the rest of the evaluation in one page: what the kit reads inside your process, what your Content-Security-Policy must allow, what a leaked project key can do, what verifying the download proves, and what the first network call is.
  • The help centre, for what Boosthis measures and how it is installed.
  • support@boosthis.com — a question about your own deployment reaches a person.