You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CSV_EXPORT.md
+57-26Lines changed: 57 additions & 26 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,9 +4,7 @@ Last updated: 2026-05-23
4
4
5
5
## Purpose
6
6
7
-
This document is the long-lived technical reference for CSV export delivery.
8
-
ADR 004 records the decision; this file explains how the export architecture
9
-
works in practice and how to plan capacity as datasets evolve.
7
+
This document is the long-lived technical reference for CSV export delivery. ADR 004 records the decision; this file explains how the export architecture works in practice and how to plan capacity as datasets evolve.
10
8
11
9
## Scope
12
10
@@ -20,13 +18,10 @@ works in practice and how to plan capacity as datasets evolve.
20
18
21
19
The export bundle is built from canonical retained files:
- Integrity failures (ciphertext size/digest, plaintext digest, wrong key) fail closed with no partial plaintext output.
185
172
- Pre-unlock export remains unavailable.
173
+
174
+
## FAQ
175
+
176
+
### Can someone use devtools to trigger CSV export before unlocking the dashboard?
177
+
178
+
Not in a way that yields plaintext data.
179
+
180
+
The export flow is wired after successful unlock, and the runtime keeps an explicit key gate (`unlockedDashboardKey`) before any export work proceeds. Even if a user manually invokes JavaScript in devtools, they still cannot produce plaintext ZIP bytes without the correct dashboard secret.
181
+
182
+
### If someone forces the click path early, will plaintext ZIP download anyway?
183
+
184
+
No.
185
+
186
+
The downloadable asset is encrypted (`.enc`). Plaintext ZIP is only produced after successful decryption with the correct key, followed by digest checks. Without the key, decryption fails and no valid plaintext ZIP download is produced by the supported runtime path.
187
+
188
+
### What checks does the browser run before download?
3. AES-GCM decrypt succeeds with the provided secret-derived key.
195
+
4. Decrypted ZIP SHA-256 matches manifest (`plaintext_sha256`).
196
+
197
+
Only then does the browser trigger the ZIP download.
198
+
199
+
### If the browser already verifies everything, why offer manual checksum copy?
200
+
201
+
Operational trust and auditability.
202
+
203
+
Some users want an extra independent verification step or need to share verification artifacts in support/debug workflows. The UI can copy a checksum line (`<sha256> <filename>`) so users can run local checks (for example `shasum -a 256 <file>.zip`, or checksum-file workflows with `-c`).
204
+
205
+
### Does this protect against all attacks?
206
+
207
+
No. It protects export payload integrity within the client-side model, but it does not replace broader supply-chain or endpoint trust.
208
+
209
+
Examples outside scope include malicious runtime JS, compromised CI/deploy pipeline, compromised browser/device, or weak secrets in `casual` mode.
210
+
211
+
### What is the practical privacy difference between `strong` and `casual` here?
212
+
213
+
Both modes keep export payload encrypted at rest/in transit in published assets. The difference is key strength policy.
214
+
215
+
-`strong`: requires high-entropy secret and is intended to resist offline guessing.
216
+
-`casual`: allows weak/non-empty secrets and is not intended to resist determined offline brute-force attempts.
0 commit comments