Privacy and personal data
This page is the overview for a data protection review: what GraphPilot records about a request, where and for how long, what your origin receives, and each setting that reduces it. Every section links the page with the detail.
Everything configurable here is per service, in gpilot.toml, and applies from the next
deployment. None of it changes what is billed.
What is recorded
Section titled “What is recorded”Every request a service serves is recorded in Insights, unless
telemetry.record leaves it out. A
recorded request keeps:
| Category | What is kept | Can be reduced with |
|---|---|---|
| Client address | The IP address as the edge saw it, plus the location and network data looked up from it: country, region, city, latitude, longitude, postal code, ISP and ASN. | telemetry. |
| Client | The User-Agent and the device type derived from it, and the client profile the request matched. | telemetry., to leave such requests out |
| Request | Method, hostname, path, size, and the correlation ID the client sent. | Not configurable |
| Headers | Only the headers the [telemetry. lists name. By default authorization and cookie are stored as a keyed digest and only a handful of descriptive headers in clear. | [telemetry. |
| GraphQL | The operation name and type, the query document, a hash of the document, and the GraphQL errors your origin answered with (message, path and code). Variable values are not stored: the request keeps a keyed hash of them. | Not configurable; see the note below the table |
| Cache | The cache status and key, the surrogate keys of the response, and the segment values that partitioned it: a vary rule’s value as a keyed pseudonym by default, the private and public values always as a keyed digest. | telemetry., pseudonymize |
| Response | Status, size, latency and the recorded response headers. The response body is not stored. | [telemetry. |
The warnings and errors the edge writes for a request are kept as its log lines. A log line carries the request’s id and the correlation ID the client sent, but no client address, location, User-Agent or client profile; those are kept with the request only. The full list of what each tab of a request shows is in the request detail panel.
Where it is stored: request telemetry and log lines are kept in the EU, for at most 90 days. Cached responses are a separate question: they are held in the edge cache wherever they were requested, with no region pinning. See where data lives.
Controlling telemetry
Section titled “Controlling telemetry”The [telemetry] section decides what is stored, without changing how a request is served or
cached. The full reference is [telemetry].
| Key | Default | Does |
|---|---|---|
record | unset (record everything) | Which requests are recorded at all. "false" records nothing. A request left out is still served, billed and counted. |
client_ip | "full" | "truncated" keeps the network part of the address and drops latitude, longitude and postal code; "none" stores no address and no region or city. |
vary_values | "pseudonymized" | "clear" stores every vary rule’s value as derived, instead of a keyed pseudonym. |
[[telemetry. | narrow lists | Which headers are recorded, and which are stored as a keyed digest (sanitize) or left out (exclude). |
[telemetry]# Leave the cluster's health probes out of Insights.record = "!starts_with(header:user-agent, 'kube-probe/')"# 203.0.113.77 is stored as 203.0.113.0.client_ip = "truncated"# The default, stated so a review can see it.vary_values = "pseudonymized"vary_values = "clear" is an explicit choice for the whole service: a rule whose value is a
session cookie, a token or an email address then stores that value in clear, and the header
sanitize rules do not cover it. See vary values.
A header list you write replaces the default one entirely, its sanitize rules included, so put
authorization and cookie first. See [telemetry.headers].
Pseudonymizing values
Section titled “Pseudonymizing values”pseudonymize(s) replaces a value with a keyed
pseudonym before the cache, your origin or your telemetry see it. It is accepted in
cache.policy.public, cache.policy.private and a vary rule’s value. Equal inputs give equal
pseudonyms within the service, so the partition still groups the same callers:
# The organization stays readable, the user id never leaves the edge.[[cache.policy.vary]]name = "x-user"value = "auth:idp.org_id + ':' + pseudonymize(auth:idp.sub)"Under the default vary_values = "pseudonymized", Insights shows a pseudonym either way;
pseudonymize then decides what the cache partitions on and what a plain origin receives.
Under vary_values = "clear", it decides which part of a value is readable. See keeping
identifiers out of Insights with
pseudonymize.
What the origin receives
Section titled “What the origin receives”Your origin receives the request as the client sent it, with these exceptions. See request headers your origin receives.
| What | By default | Change it with |
|---|---|---|
| The client’s IP address | Not sent. | origin.forward_client_ip = true sends it in gp-client-ip. |
Forwarding headers a client sent (X-, Forwarded, X-Real-IP and others) | Removed. | origin. |
| A vary rule’s derived value | Not sent. | pass_: "anonymized" sends a keyed v1= digest, "plain" the value itself. |
Any other header or cookie, Authorization included | Sent unchanged. | [origin. and [origin. hide or only lists. |
# The origin learns a stable, opaque id per shopper, and never sees the token.[[cache.policy.vary]]name = "x-shopper"value = "pseudonymize(auth:idp.sub)"pass_to_origin = "plain"
[origin]headers = { hide = ["authorization"] }A filter in [origin.headers] applies just before the request is forwarded, after the
credential was verified and the cache key was built. With the credential hidden, have your origin
check the request signature to know a request came through
GraphPilot.
Combining the options
Section titled “Combining the options”pseudonymize, a vary rule’s pass_to_origin and telemetry.vary_values act one after the
other, each on the result of the one before:
- The rule’s value is derived.
pseudonymizereplaces its argument right there, so the derived value already holds the pseudonym. The cache key is built from this derived value. - The origin receives it as
pass_to_originsays:"plain"sends the derived value,"anonymized"a keyedv1=digest of it,"never"nothing. - Insights records it. A rule at
"anonymized"records exactly thev1=value the origin received, whatevervary_valuessays. A rule at"plain"or"never"followsvary_values:"pseudonymized"(default) recordshmac-sha256:and 16 hex digits of the derived value,"clear"the derived value itself.privateandpublicvalues are always recorded as a keyed digest.
For a vary rule with value = "pseudonymize(auth:idp.sub)":
pass_ | vary_values | The origin receives | Insights records |
|---|---|---|---|
plain | pseudonymized | The pseudonym | A digest of the pseudonym |
plain | clear | The pseudonym | The pseudonym |
anonymized | either | A v1= digest of the pseudonym | The same v1= digest |
never | pseudonymized | Nothing | A digest of the pseudonym |
never | clear | Nothing | The pseudonym |
Hashing a pseudonym a second time is harmless: both steps are deterministic and use the
service’s key, so segments group and the cache hits exactly as with one step, and no clear value
appears anywhere. It also adds nothing. pseudonymize earns its place in two cases:
- With
pass_to_origin = "plain", when the origin needs a stable value per user without learning who the user is. - With
vary_values = "clear", to keep one part of a value readable, such as the organization, and hide the rest.
With "anonymized", or with the default vary_values and a rule at "never", it is redundant.
Retention and deletion
Section titled “Retention and deletion”| What | How long |
|---|---|
| Request telemetry in Insights | 1 day on Glider (Free), 7 days on Jet, 30 days on Airliner, 90 days on Jumbo. The GraphQL errors of a request go with it; error groups and filter suggestions are deleted once they have not occurred within the plan’s retention. See what Insights needs. |
| The raw telemetry copy | At most 90 days per file, in the EU, used only to rebuild Insights. See the raw telemetry copy. |
To delete earlier:
- Reset history. An Admin can delete a service’s request telemetry, purge history and alert history at any time in the portal, under Settings → General, without deleting the service. The request counts billing is based on and every invoice are kept. See reset history.
- Delete a service, an organization or your account. Its telemetry, logs and stored files stop being readable at once and are deleted within minutes, once each service is off the edge. See delete the service, delete an organization and delete your account.
A deletion or a reset keeps some records on purpose: the billing and invoice history, the record of the accepted terms, the raw telemetry copy and backups until they expire. The full list is what a deletion keeps.
A data-minimal setup
Section titled “A data-minimal setup”-
telemetry.client_ip = "truncated"or"none". -
telemetry.vary_valuesleft at"pseudonymized". -
telemetry.recordleaves out traffic you do not need, such as health probes. - Every replaced header list starts with the sanitize rules for credentials.
- Personal parts of a partition value wrapped in
pseudonymize. - Vary rules at
pass_to_origin = "never"or"anonymized", unless the origin needs the value. -
origin.forward_client_ipleft off, unless the origin needs the address. - Personal values sent as GraphQL variables, not written into the document.
The worked example, with what Insights then shows, is keeping request telemetry minimal.
What a service records and passes on follows from its configuration, and the responsibility for
that configuration, such as choosing vary_values = "clear", lies with you. How GraphPilot
processes personal data on your behalf is set out in the data processing
agreement.