DEV Community

Cover image for Exports as a security boundary from local record to shareable report
CrisisCore-Systems
CrisisCore-Systems

Posted on • Edited on • Originally published at dev.to

Exports as a security boundary from local record to shareable report

Exports are the point where private local records become shareable artifacts. This post explains boundary controls, review steps, and failure handling so sharing can be intentional and auditable.

Summary Boundary controls for moving from local records to shareable exports with review and rollback steps.

Start Here

Exports are where the trust model changes

In a local-first app, the default promise is usually:

your data stays on your device

Exports are the moment that promise becomes conditional.

Because as soon as you create a file:

  • it can be emailed
  • it can be uploaded to cloud backups
  • it can be forwarded
  • it can be printed
  • it can sit in Downloads forever

That’s not a reason to avoid exports.

It’s a reason to treat export like a deliberate boundary in both code and UX.


Pain Tracker’s export stance: explicit, local, and user-triggered

The core export utilities live here:

  • src/utils/pain-tracker/export.ts

The UI that invokes them (with filters) is here:

  • src/components/export/DataExportModal.tsx

The pattern is intentionally boring:

1) user chooses a format (CSV/JSON/PDF)
2) user optionally filters the date range / symptoms / locations
3) app generates a string (or PDF data URI)
4) app downloads it via a normal browser download

No background exporting, no scheduled exports, no “send to provider” button that quietly turns into a network feature.


The real boundary is “create a file”

Pain Tracker downloads data using a small helper:

  • downloadData(data, filename, mimeType) in src/utils/pain-tracker/export.ts

It creates a Blob, then triggers a download by clicking an <a> element programmatically.

That’s important because it’s a clear, user-observable browser action:

  • you can see the file land
  • you can delete it
  • you can decide where it goes

It’s a simple boundary you can explain to a tired user.


Exports are not “safe” by default (and shouldn’t pretend to be)

The CSV and JSON exports can include notes, and notes are the highest-risk field in most journaling apps.

You can see that directly in the implementation:

  • CSV includes a Notes column and escapes quotes
  • JSON is literally JSON.stringify(entries, null, 2)

This is good honesty:

  • the export is a faithful copy
  • the app isn’t claiming it can “anonymize” your narrative

If you need de-identification, that’s a different feature with a different risk profile.


Treat “tracking exports” as a separate, minimal channel

Even with no backend, teams often want to answer questions like:

  • do people use exports?
  • which formats matter?

Pain Tracker uses two kinds of tracking around exports:

1) Local-only usage tracking

  • trackExport(type, recordCount) in src/utils/usage-tracking.ts
  • it stores the last ~100 export events in localStorage
  • it stores counts, not content

It also sanitizes metadata so Class A fields aren’t stored in plaintext localStorage.

2) Optional GA4 events

  • export utilities call trackDataExported(format, entryCount)
  • those events are gated behind env + explicit consent (covered in Part 8)

The key point is what’s not tracked:

  • not the file contents
  • not notes
  • not the “what did you export” payload

UX: don’t make export feel like a trap

In Pain Tracker, export is presented as:

  • an explicit action in an export modal
  • a user-visible download

Good export UX in sensitive apps is mostly about preventing regret:

  • clear format labels (CSV vs JSON vs PDF)
  • clear file naming
  • an obvious success state
  • no surprise side effects

If you add “share” features later, treat them as new boundaries: they turn a local file into network exposure.


Next up

Part 7 covers WorkSafeBC-oriented workflows — and how to keep language careful and grounded in what the repo actually does.

Prev: Part 5 — Trauma-informed UX + accessibility as architecture
Next: Part 7 — WorkSafeBC-oriented workflows (careful language)


Support this work

Top comments (0)