Session Replay: Masking & Privacy
How ScryWatch's configurable masking works — the Blocklist and Strict modes, tag-based masking, the always-on floor, and configuring rules from the dashboard without an app release.
Session Replay: Masking & Privacy
Session Replay masking is configurable per project — you choose a masking mode in the dashboard, tag the elements that matter in code, and decide what’s actually masked without shipping a new build. This page explains the masking model in detail — read it before enabling Session Replay on any screen that shows user data.
Two masking modes
Every project runs in one of two modes, set from the masking editor in the dashboard:
Blocklist — capture, mask what’s tagged (default)
Your app records normally. ScryWatch masks anything you’ve tagged or explicitly marked to mask, plus the always-on floor described below. A screen you haven’t tagged records in the clear, except for the floor.
This is the default mode for new projects. It gives you the best fidelity and is the easiest to adopt — most apps only need to tag the handful of screens that actually show sensitive data.
Strict — deny-by-default (opt-in)
Everything is occluded by default, except elements you’ve explicitly marked as safe in code with ScrywatchReveal, plus the floor. Nothing leaks by default. Turn this on for regulated apps (health, finance, etc.) where you’d rather review every element before it’s ever visible in a replay.
Switching modes in the dashboard takes effect on the next session — no app release required. See Configuring masking from the dashboard.
Always-on floor (both modes)
One category is masked unconditionally, in both Blocklist and Strict mode, and cannot be revealed by any code marker or dashboard rule:
- Password / secure fields — any
TextField/TextFormFieldwithobscureText: true. What a user types into a password field must never land in a stored frame.
That is the entire always-on floor. Everything else records by default in Blocklist mode and is opt-in to mask:
- PII text (email, card/PAN, SSN, phone) — not masked automatically. Add a Text pattern rule (
email,card,ssn,phone) in the masking editor to mask it. - WebViews / native surfaces — not masked automatically. Add a Widget type rule (
webvieworvideo), or wrap them inScrywatchMaskin code. (They also can’t be captured meaningfully — their pixels come from outside Flutter’s render tree.)
If you’d rather mask PII, WebViews, and everything else without configuring individual rules, switch the project to Strict mode, which masks everything by default.
Heads up: In Blocklist mode, sensitive content other than password fields records in the clear unless you add a rule for it. Add the
textPattern/widgetTyperules your app needs — or use Strict mode — before recording real users on screens that show regulated data.
The developer API
You control masking in code with three widgets, all imported from scrywatch_replay:
import 'package:scrywatch_replay/scrywatch_replay.dart';
ScrywatchTag — label an element for dashboard-driven masking (recommended)
Wrap an element with a stable string tag. Tagging doesn’t decide mask or reveal by itself — it just labels the element. Whether that tag is actually masked is decided in the dashboard, so you can change what’s masked without an app release:
ScrywatchTag(
'account-balance',
child: BalanceCard(account: account),
)
Tags your app emits are reported to ScryWatch automatically and show up in the dashboard’s rule picker, so you don’t have to remember exact tag strings when you set up a rule later.
ScrywatchTag is the recommended way to handle most masking: tag anything that might be sensitive as you build it, then decide in the dashboard whether each tag should actually be masked — per project, without redeploying.
ScrywatchMask — force-mask a widget
Wrap anything you want to guarantee is occluded, in either mode, regardless of dashboard configuration:
ScrywatchMask(
child: BalanceCard(account: account),
)
This is a hard, in-code rule — use it for content that should never be visible in a replay no matter how the project’s masking is configured.
ScrywatchReveal — mark a widget eligible to be shown
Wrap UI you’ve reviewed and confirmed contains nothing sensitive, to make it eligible to be shown:
ScrywatchReveal(
child: AppBarLogo(),
)
ScrywatchReveal is required in Strict mode — without it, everything stays masked. In Blocklist mode it has no practical effect, since unmasked is already the default there.
ScrywatchReveal is also a safety “double-key” between code and dashboard: a dashboard rule can only ever add masking, never remove it, so the only way an element becomes visible is if the code has marked it eligible with ScrywatchReveal and no mask rule (tag, widget type, or text pattern) currently matches it. The dashboard can never reveal something the code didn’t green-light, and it can never touch the always-on floor.
Warning: None of these three widgets can un-mask a password /
obscureTextfield — the always-on floor stays masked even inside aScrywatchRevealsubtree.
Configuring masking from the dashboard
Open Replay → Masking (app.scrywatch.com/replay/masking) for a project to configure masking without touching code or shipping a release.
Mode toggle
Switch the project between Blocklist and Strict. This changes the fallback for anything not otherwise tagged or marked — see Two masking modes above.
Mask rules
Add rules that tell ScryWatch what to mask. Each rule matches on one of three things:
- Tag — pick from a list of tags your app has already emitted via
ScrywatchTag, or type a new one. This is the most common rule type: tag it in code once, then flip it on or off per project from here. - Widget type — mask every instance of a kind of element with zero code changes, e.g. “mask all images” or “mask all text inputs” (image, text input, webview, video).
- Text pattern — mask text matching a pattern: email, card number, SSN, phone, or a custom pattern you define.
Rules are add-only: the dashboard can create new masking, but it can never reveal something. Combined with the ScrywatchReveal double-key described above, this means a dashboard rule change can only make a replay more private, never less.
Changes to mode or rules take effect on the next session — there’s no app release or redeploy involved.
The always-on floor is listed in the editor as permanent and non-editable — it isn’t a rule you can turn off, it’s just shown there for visibility.
Where redaction happens
All masking is applied on-device, before a frame is encoded or uploaded — the SDK paints occlusion onto the captured frame itself, not as a network-side filter. No unmasked pixel data ever leaves the phone. This is true in both modes: whichever mode and rules are active for a project, redaction still happens locally before anything is transmitted.
Compliance posture
- GDPR / CCPA — personal data you tag, plus everything caught by PII detection, is redacted before it’s transmitted, minimizing what ScryWatch ever processes or stores.
- HIPAA — for protected health information, run the project in Strict mode so screens are occluded until explicitly reviewed and revealed, rather than relying on tagging every sensitive element in Blocklist mode.
- PCI — card numbers are caught by PII pattern detection and payment/password fields are masked unconditionally, in either mode.
Because masking happens on-device, there’s no unmasked copy sitting in a queue waiting to be scrubbed server-side, regardless of which mode a project uses.
Note: You are still responsible for reviewing your own screens. Neither mode replaces a privacy review — Blocklist requires you to tag what’s sensitive, and Strict requires you to review what you reveal with
ScrywatchReveal.
Related docs
- Flutter SDK Setup — where
initandsetConsentfit into this - Viewing Replays — how masked regions appear in playback