# ADR-035: Screenshot Protection Uses CSS/JS Deterrence — OS-Level Enforcement Is Not Available in PWA

## Status

Accepted, 2026-06-22.

## Status History

```yaml
status_history:
  - date: 2026-06-22
    status: Proposed
    changed_by: hkl
    reason: Formalising the decision after user request for screenshot prevention in sensitive PWAs
    changed_via: adr-kit (360lm)
  - date: 2026-06-22
    status: Accepted
    changed_by: hkl
    reason: Deterrence-only approach implemented; users informed of its limits
    changed_via: adr-kit (360lm)
```

## Context

Certain data displayed in 360lm PWAs is sensitive: salary slip amounts, client contact details, custodian transfer records. Employees or field agents may share screenshots of this data inappropriately. The request was made to "prevent screenshots" in these PWAs. The browser/PWA environment (whether installed as a PWA or opened in a browser tab) runs in user space — it cannot call OS-level screenshot APIs or prevent the OS from capturing the screen. DRM/EME applies only to media streams, not arbitrary DOM content.

## Decision

360lm PWAs implement **CSS/JS screenshot deterrence**, not enforcement:

- `user-select: none` on sensitive containers (prevents copy-paste of text, reduces screenshot utility but does not block screenshots).
- `document.addEventListener('visibilitychange', blurContent)` — sensitive content is blurred when the PWA loses focus (covers screen-recorder screen-share scenarios).
- **Confidential mode toggle** in Admin PWA: enables/disables deterrence per role. When enabled, sensitive fields are partially masked (last 4 digits of amounts shown, rest blurred).
- No `<canvas>` overlay tricks — these create false confidence and add implementation complexity for zero security gain (screenshots capture canvas pixels identically to DOM).

Users and admins must be informed that this is deterrence only, not enforcement. A determined user can bypass all CSS/JS deterrence.

**Decision Maker:** hkl

## Alternatives Considered

- **OS-level screenshot prevention.** Rejected: not available in browser/PWA context; browsers do not expose screenshot prevention APIs to web applications; this is a platform capability limitation, not a code gap.
- **DRM / EME (Encrypted Media Extensions).** Rejected: EME applies to video/audio streams served via encrypted media protocols; it has no effect on DOM content, text, or `<img>` elements; misapplication would be misleading security theatre.
- **Canvas overlay that replaces DOM content (prevents DOM inspector, not screenshots).** Rejected: canvas pixels are captured identically by OS screenshot tools; the overlay prevents copy-paste but not screenshots; adds rendering complexity and accessibility issues.
- **Server-side PDF generation with watermark (employee name embedded).** Partial future option: watermarked PDFs are a deterrence + accountability measure (not prevention); can be added for salary slips if policy requires; not implemented yet. // ponytail: upgrade trigger=policy decision by management to require watermarked PDFs

## Consequences

**Positive:**
- `user-select: none` and blur-on-hide are lightweight, zero-dependency additions.
- Confidential mode toggle lets Admin PWA enable/disable per role without code changes.
- No false promises — users are informed that screenshots are possible.

**Negative / Trade-offs:**
- Not a security control — cannot be relied on in a threat model where screenshots are a genuine attack vector.
- Blur-on-visibilitychange is visible/disorienting for legitimate use (switching apps to copy a reference number): Admin can disable if this causes friction.
- `user-select: none` blocks accessibility tools that rely on text selection (screen readers may be affected).

**Risks and mitigations:**
- Management or users assume PWA "prevents" screenshots and rely on this as a security control: mitigated by explicit communication in Admin settings UI ("Deterrence mode — does not prevent screenshots") and this ADR for developers.
- Screen-recorder captures unblurred content during the brief transition moment: accepted risk; blur fires on `visibilitychange` which is near-instantaneous.

## Related Decisions

- ADR-013 (single HTML file, no framework) — deterrence is implemented in vanilla JS with no additional libraries.
- ADR-011 (PIN-based auth) — employee identity is established at PIN login; watermark accountability is an extension of the identity model.

## References

- `memory/dbt_admin.md` — confidential mode toggle, role-based deterrence settings
- `memory/vcc_library.md` — UX Decisions log for sensitive data display
