Consent and GDPR
Every record Belemetry produces belongs to exactly one of twelve features, and a feature is collected only while it is both enabled by configuration and granted by the user. Nothing is sent before the user decides, each feature can be granted and revoked on its own at runtime, and revoking one purges what it queued and what it stored on the device.
Your consent, live
This panel is wired straight to IBelemetryConsent - the same service the site's banner
uses. Tick a feature to grant it, untick it to revoke it, or set a whole group at once. The decision is
remembered in this browser, and the Live telemetry page shows what each grant lets
through.
Per feature
Groups
- Available
All (4095)- Granted
None (0)- IsDecided
- not decided
Effective (configured and granted)
Shown once the page is interactive.
Features
The bit values are a wire contract shared by C# and the browser script: they cross the interop boundary as plain integers and are persisted in the browser, so they never change.
| Feature | Bit | Covers |
|---|---|---|
Errors |
1 | JS errors and unhandled rejections, .NET logs at Error/Critical or with an exception, TrackException |
PageViews |
2 | Page views - navigation and TrackPageView |
Interactions |
4 | Clicks and other interactions |
Performance |
8 | Web vitals, document load, resource timing, long tasks |
Dependencies |
16 | Browser fetch and XHR spans |
Sessions |
32 | A session id that survives reloads (sessionStorage) |
UserTracking |
64 | enduser.pseudo.id (localStorage) and the authenticated user.id |
Logs |
128 | .NET logs below Error, browser console capture |
Traces |
256 | .NET Activity spans |
Metrics |
512 | .NET Meter metrics |
Persistence |
1024 | Unsent telemetry kept on the device for a later retry |
Events |
2048 | Custom events (TrackEvent) |
Groups
Banners rarely ask about twelve things. The groups are the usual three questions, as flags you can pass
to SetAsync or combine with others:
| Group | Features | Answers |
|---|---|---|
Diagnostics | Errors, Logs, Traces, Dependencies | What is needed to troubleshoot the app |
Analytics | PageViews, Interactions, Sessions, UserTracking, Events | How the app is used |
Monitoring | Performance, Metrics | How the app performs |
All | Every feature (4095) | For apps that need no consent |
Persistence is in no group: keeping unsent telemetry on the device is a question of its
own. Grant it with GrantAsync(BelemetryFeatures.Persistence), or include it in what you
pass to SetAsync.
How consent behaves
- Effective = configured AND granted.
BelemetryOptions.Features(all by default) is what the app collects at all;IBelemetryConsent.Availablereports it. A feature switched off by configuration is never collected, whatever is granted. - Opt-in by default. Before the user decides, only
Consent.Defaultis granted -Noneunless you set it, for example toErrorswhen error reporting is covered by legitimate interest, or toAllfor an app that needs no consent. - Nothing undecided leaves the device. Records of features not yet granted are held in
memory, up to
Consent.PendingLimit(500). When the user decides, what they granted is sent and the rest is dropped. - Revoking is immediate. Queued records of a revoked feature are discarded, and its
device storage cleared: revoking
Persistenceclears the offline queue,Sessionsthe stored session,UserTrackingthe pseudo id and the user id. - The choice is remembered (
Consent.Persist, on by default) inlocalStorageunderbit-belemetry-consent, and in a file in Hybrid apps. Recording the choice itself is strictly necessary, so it needs no consent of its own. - Global Privacy Control is honored (
Consent.HonorGlobalPrivacyControl, on by default): a browser sending GPC never gets theAnalyticsfeatures, whatever is requested. - No cookies. Belemetry sets none.
- Trace context still flows. Without
Traces, activities are still created for propagation, so downstream services keep receiving the trace context - but nothing is recorded.
On WebAssembly and Hybrid consent is app-wide. On Blazor Server it belongs to the circuit's user - it
lives in their browser - so IBelemetryConsent needs an interactive render mode there. The
server pipeline is governed by Server.Features instead, since it serves every user at once.
builder.Services.AddBitBelemetryServices(options =>
{
options.Features = BelemetryFeatures.All; // what the app collects at all (default)
options.Consent.Default = BelemetryFeatures.None; // granted before a decision (default)
options.Consent.Persist = true; // remember the choice (default)
options.Consent.HonorGlobalPrivacyControl = true; // GPC: never Analytics (default)
options.Consent.PendingLimit = 500; // held until the decision (default)
});A banner of your own
A banner needs three things from IBelemetryConsent: whether to show itself
(IsDecided), a way to record the answer (SetAsync), and
Changed to hide itself when the answer comes from elsewhere - a settings page, or another
tab. It should wait for the first interactive render and RefreshAsync: the decision lives
in the browser, and a prerendered banner would flash at every user who already chose.
@implements IDisposable
@inject IBelemetryConsent Consent
@if (_ready && Consent.IsDecided is false)
{
<div class="consent-banner" role="dialog" aria-label="Telemetry consent">
<p>We measure errors and usage to improve the app. Nothing is sent until you choose.</p>
<button @onclick="() => Consent.SetAsync(BelemetryFeatures.All)">Accept all</button>
<button @onclick="() => Consent.SetAsync(BelemetryFeatures.Diagnostics)">Diagnostics only</button>
<button @onclick="() => Consent.SetAsync(BelemetryFeatures.None)">Reject</button>
</div>
}
@code {
private bool _ready;
protected override void OnInitialized() => Consent.Changed += OnChanged;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender is false) return;
await Consent.RefreshAsync();
_ready = true;
StateHasChanged();
}
private void OnChanged(object? sender, EventArgs e) => _ = InvokeAsync(StateHasChanged);
public void Dispose() => Consent.Changed -= OnChanged;
}
Outside .NET - on a static SSR page, or in plain JavaScript - the script has the same API:
API
IBelemetryConsent
Interface
Reads and changes the user's consent at runtime, per BelemetryFeatures - the API behind a consent banner or a privacy settings page.
| Name | Type and default | Description |
|---|---|---|
Available |
BelemetryFeatures |
The features that can be consented to - the configured BelemetryOptions.Features. |
Granted |
BelemetryFeatures |
The features currently granted - always a subset of IBelemetryConsent.Available, so these are collected. |
IsDecided |
bool |
Whether the user has made a choice (or one was restored from a previous visit). |
Changed |
EventHandler |
Raised after the granted features or the decided state changed. |
IsGranted(BelemetryFeatures feature) |
bool |
Whether every flag of feature is granted. |
SetAsync(BelemetryFeatures features) |
ValueTask |
Records the user's decision: exactly features (those of them that are IBelemetryConsent.Available) are granted, everything else is revoked (queued data of revoked features is discarded, and their device storage cleared). |
GrantAsync(BelemetryFeatures features) |
ValueTask |
Grants features in addition to what is granted. |
RevokeAsync(BelemetryFeatures features) |
ValueTask |
Revokes features, keeping the rest. |
RefreshAsync() |
ValueTask |
Reloads the state from the browser (e.g. after the first interactive render on Blazor Server). |
BelemetryFeatures
Enum
The independently consentable parts of Belemetry. Every telemetry record belongs to exactly one feature, and a record is only sent while its feature is both enabled by configuration and granted by the user.
| Value | Number | Description |
|---|---|---|
None |
0 |
Nothing. |
Errors |
1 |
JS errors and unhandled rejections, .NET logs at Error/Critical or carrying an exception, and TrackException. |
PageViews |
2 |
Page views - history and enhanced navigation, and TrackPageView. |
Interactions |
4 |
Clicks and other user interactions. |
Performance |
8 |
Web vitals (LCP, INP, CLS, FCP, TTFB), the document-load span, resource timing and long tasks. |
Dependencies |
16 |
Browser fetch/XHR client spans. |
Sessions |
32 |
A session id that survives page reloads (kept in sessionStorage). Without it the session id lives in memory only. |
UserTracking |
64 |
The pseudonymous user id (enduser.pseudo.id, persisted on the device) and the authenticated user id (user.id). |
Logs |
128 |
.NET logs below Error, and browser console capture below error. |
Traces |
256 |
.NET Activity spans. |
Metrics |
512 |
.NET Meter metrics. |
Persistence |
1024 |
Keeping unsent telemetry on the device (browser storage or files) to retry it after going offline or restarting. |
Events |
2048 |
Custom events (TrackEvent). |
Diagnostics |
401 |
Errors, logs, traces and dependencies - what is needed to troubleshoot the app. |
Analytics |
2150 |
Page views, interactions, sessions, user tracking and custom events - what describes how the app is used. |
Monitoring |
520 |
Web vitals and metrics - how the app performs. |
All |
4095 |
Every feature. |
BelemetryConsentOptions
Class
How consent is obtained and kept.
| Name | Type and default | Description |
|---|---|---|
Default |
BelemetryFeatures= BelemetryFeatures.None |
The features granted before the user has decided. Defaults to BelemetryFeatures.None (opt-in: nothing is sent until consent is given). Set it to e.g. BelemetryFeatures.Errors when error reporting is covered by legitimate interest, or to BelemetryFeatures.All for apps that do not need consent at all. |
Persist |
bool= true |
Whether the user's choice is remembered on the device (localStorage key bit-belemetry-consent, which records the choice itself and is therefore strictly necessary). Defaults to true. |
HonorGlobalPrivacyControl |
bool= true |
Whether a browser signalling Global Privacy Control (navigator.globalPrivacyControl) never gets the BelemetryFeatures.Analytics features, whatever is requested. Defaults to true. |
PendingLimit |
int= 500 |
How many records of not-yet-granted features are held in memory while the user has not decided, to be sent if granted and dropped if not. Defaults to 500. |