Security and privacy

What your SDKs send featflags and what they don't, what browsers and apps can see, how experiment data stays pseudonymous, and how access is controlled.

What SDKs send us

Nothing about your users. SDKs evaluate flags in your own process, against a copy of your flags they download, so the contexts you pass, with their keys, emails and other attributes, never leave it.

What they do send, about once a minute, is counts: how many times each flag served each variant, with the reason, the rule that matched and the version of your flags that served it, under the SDK key and the SDK’s name and version.

{
  "key": "pk_…",
  "sdk": "js/0.1.0",
  "counts": [{ "flag": "new-checkout", "variant": "on", "reason": "rule", "rule_id": "r1", "version": 1791367200000, "n": 412 }]
}

These counts power each flag’s evaluation charts and statuses. They carry no context data, and featflags dates them by when they arrive, not by your users’ clocks.

The exposure callback some SDKs offer, which fires the first time a user sees a variant, runs in your code, for your own analytics. featflags doesn’t receive those.

Experiments

When you run an experiment, your SDKs also send, for that experiment only:

  • a unit ID for each user who saw it: a keyed hash of the user’s key, computed with your environment’s experiment key. You generate that key and keep it; featflags stores only a fingerprint of it, so featflags can’t recompute an ID or tell whose it is. Each experiment derives its own IDs, so the same person has unrelated IDs in two experiments, and experiments can’t be joined into a profile;
  • the variant they saw;
  • the events you chose to track, each a name and an optional number, such as checkout.completed and 49.90;
  • the minute each happened.

Nothing else about the person, their device or the request is stored with them: no attributes, no IP address, no user agent, no page. Per-user rows are deleted 30 days after the experiment stops; the results keep only totals per variant. Event names and values are yours to choose, so keep personal data out of them: an event called purchase.alice would give away who it is.

In browsers and mobile apps, signed-in users’ IDs are computed from seeds your backend hands over, so the experiment key stays on your servers. An app that holds the key itself, for anonymous visitors, ships it to everyone who has the app.

What browsers and apps can see

A client key (pk_…) is public, and so is everything it reads. Only flags you mark client-visible are sent to browsers and mobile apps, with their rules, so anyone who inspects your app can read them. Keep sensitive targeting, such as a rule naming an unannounced customer, in server-only flags.

  • Target keys are hashed. The keys in individual targets and segment lists reach SDKs as salted hashes, never in plain text. That stops casual reading, but someone who can guess a key, such as a sequential ID or a known email, can check whether it’s listed.
  • Private segments can’t be checked that way at all: an app can only test a private segment for a user your backend has issued a token for, so their members can’t be enumerated.
  • Allowed origins stop the browser SDK from serving flags on websites you didn’t list. They guard against mistakes, not attackers: anyone can still fetch the flags directly.

Server keys (sk_…) read every flag, and the secrets private segments need, so keep them out of browsers, apps and source control. Only people with write access to an environment can see its server keys.

When we’re down

SDKs read flags from a CDN, never from featflags itself, and keep the last flags they had when they can’t reach it. Browsers and mobile apps also keep them on the device, so even a cold start offline evaluates your flags. If featflags has an outage, your apps keep serving flags as they were; you can’t change them until we’re back. A failed payment restricts the app and the API, never the flags your SDKs read.

Access

  • Sign-in is through Google, GitHub or, on Business, your own identity provider with single sign-on, which you can make the only way in for your domains.
  • Roles (reader, writer, admin, owner) apply to the whole organisation, one project or, on Business, one environment. Reading a production environment’s server keys needs write access to it.
  • Approvals and required comments per environment: see Approvals and automation.
  • Credentials (API tokens, SDK keys, trigger URLs, webhook secrets) are shown once, when they’re created. featflags stores API tokens, trigger URLs and session cookies only as hashes.
  • History records every change with its author and comment, and the audit log adds sign-ins, tokens and AI assistant connections across the organisation.
  • AI assistants you connect act with your access or read-only, through the same checks as you, and you can disconnect them at any time: see MCP.
  • featflags support can look inside your organisation when helping you. Every read it makes appears in your audit log, and every change it makes in your history, under its own name.
  • Webhooks and audit streams are signed, and only ever sent to public internet addresses.

Your data

  • Export the whole organisation at any time, even when the account is read-only: projects, flags, segments, list keys, members, history and analytics, as a zip, from Settings or with featflags export.
  • Delete the organisation in its settings. It disappears at once, its flags keep being served for 30 days in case you change your mind, and then everything is purged, including what SDKs download. You can delete your own account once you’ve left every organisation.

To sign you in, we keep your email address and the identity your Google or GitHub account returns. The privacy policy is being written; until it’s published, it lists how the product handles data, which won’t change. This site sets no cookies and runs no trackers.