--- /** * `/legal/privacy/` — build step 10. Spec: docs/01 §`/legal/*`, * docs/05-backend-spec.md §Privacy policy must state. * * ⚠️ **THE GOVERNING INSTRUCTION IS "WRITTEN TO MATCH WHAT IS ACTUALLY BUILT, * NOT WHAT IS TYPICAL" — docs/05 — AND THAT IS WHY THIS PAGE IS BUILT LAST IN * THE ORDER.** `docs/01`: *"/legal/* — written to match the backend as actually * built."* On this page a sentence that describes an intended control rather than * a real one is a false statement to the public in a legal document, and it is * the kind that fails silently: nothing breaks, and the sentence reads correctly. * * So three things are DERIVED rather than written, and each closes a specific * way this page could quietly become untrue: * * 1. **The list of what is collected is rendered from `INTAKE_FIELDS`** — the * same array `/contact/` builds the form from. A field added to the form * appears here on the same build. A hand-written list is the copy nobody * re-reads, which is the SES-DKIM shape in a document with legal weight. * 2. **The retention period is rendered from `RETENTION_MONTHS`**, which is the * figure `backend/intake/handler.mjs` writes into the `ttl` attribute. * docs/05: *"Whatever number ships must match `/legal/privacy/` exactly."* * 3. **The analytics paragraph is rendered from `ANALYTICS.installed`.** D15 * decided Plausible; §7 records that no script is on any page. Deciding is * not installing, and a policy naming a processor that processes nothing is * a false disclosure. Today it says there are none. * * ⚠️ **WHAT THIS PAGE DELIBERATELY DOES NOT CLAIM, AND THE OMISSIONS ARE THE * POINT.** docs/05 specifies a customer-managed KMS key, point-in-time recovery, * and DynamoDB TTL. `AGENTS.md` §7 is the register for whether each of the three * is enabled, and **this comment does not restate what it says** — it did once, * went stale within the day, and had to be pulled back (§12 R19). So: * * - "Encrypted at rest" IS stated, because DynamoDB encrypts every table at * rest unconditionally — it is true whether or not the customer-managed key * in docs/05 has been configured. * - The customer-managed key and point-in-time recovery are NOT mentioned. * Neither is a fact a reader needs, and neither is verified. * - **Automatic deletion IS stated, and it asserts a MECHANISM rather than only * a period** — the one promise here whose truth lives entirely outside this * repository. The handler writes the `ttl` attribute, and ⚠️ **writing the * attribute is not the mechanism**: TTL must also be enabled on the table, * which §7 records — **and the setting being on still does not prove a * record is ever deleted.** Only a record written with a near-future `ttl` * and watched to vanish proves that. docs/05's definition of done carries * "TTL set and verified by test record" and `docs/06`'s cutover checklist * names this page as what that item protects. ⚠️ **This read "both halves * before this page is public" and the page went public first — Pouya's * ruling of 2026-09-03: publish, then confirm the deletion, reading from * 2026-09-04.** So the second half is now owed rather than pending, which is * a weaker position and is recorded as one. See the comment on the retention * section below, and §9 Q60. * * ⚠️ **NO LICENSURE CLAIM AND NO ANSWER TO THE CAPACITY QUESTION.** A privacy * policy is where "legal advice" phrasing arrives by convention. §4 records * licence status as `[unestablished]` and instructs this repository to answer * neither way; `docs/03`'s ratified pattern is role, then consequence for the * reader, and no verb of capacity. Applied throughout. */ import BaseLayout from '../../layouts/BaseLayout.astro'; import Eyebrow from '../../components/Eyebrow.astro'; import Undertaking from '../../components/Undertaking.astro'; import { getImage } from 'astro:assets'; import ogDefault from '../../assets/og-portrait.jpg'; import { pageGraph } from '../../data/schema'; import { ANALYTICS, CONDUCT_UNDERTAKINGS, CONTACT, SITE, } from '../../data/site'; import { INTAKE_FIELDS } from '../../data/intake'; const ldImage = await getImage({ src: ogDefault, format: 'jpeg', width: 1200, height: 630, }); const graph = pageGraph(new URL(ldImage.src, Astro.site).href); /** * ⚠️ MUST MATCH `RETENTION_MONTHS` IN `backend/intake/handler.mjs`, which is * the figure written into the record's `ttl`. docs/05: "Whatever number ships * must match /legal/privacy/ exactly." The handler is a separately deployed * artefact and cannot be imported here, so this is a second copy — and unlike * the intake field tables there is no `check:` script over it. Treat a change to * either as a change to both, and see docs/06's cutover checklist. */ const RETENTION_MONTHS = 24; /** Bump this on ANY substantive edit. A privacy policy with a stale date is a * policy a reader cannot tell they are reading an old version of. */ const LAST_UPDATED = '4 September 2026'; /* Rendered from the form's own field list, so the two cannot drift. `consent` and the honeypot are absent from `INTAKE_FIELDS` deliberately and are described in prose below instead — one is not information about the inquirer, and the other is not information at all. */ const COLLECTED = INTAKE_FIELDS.map((field) => field.label); ---
Privacy

What the intake form collects, and for how long.

This describes what actually happens to what you send me, not what is typical. Last updated {LAST_UPDATED}.