Skip to content

PII, legal text, and GDPR runtime

Show Pony collects guest names and optional emails on the public RSVP form. That is personal data — but this tutorial app deliberately splits two concerns that newcomers often conflate:

ConcernWhat it isShow Pony chapter
Legal disclosureStatic Impressum / Privacy / Terms on the apex13 — Legal pages
Operational GDPRExport, erasure, retention, restriction for live dataThis chapter + user-data-rights demo

Mira shares a link; guests type a name (required) and sometimes an email (optional, for the confirmation mail from chapter 10). Those rows land in the rsvp entity scoped to her tenant.

What she needs in production:

  1. Privacy policy that describes what is collected — covered by legal pages.
  2. Technical protection for guest PII at rest (encryption / KMS).
  3. Data-subject rights — a guest asks “delete my RSVP” or “send me everything you stored about me”.

Show Pony ships (1) and a searchable demo of the guest list. It does not mount the full GDPR runtime stack — that would distract from the RSVP tutorial. Instead it documents the trade-offs and points to the dedicated sample.

Alex RSVP’d with name + email, then emails Mira: “Please remove my data.” Without a user account, there is no self-service “Delete my account” button — Mira (or your support flow) must trigger erasure against the rsvp row.

Why Show Pony stops short: wiring user-data-rights + export/forget hooks is real product work (entity extensions, retention cron, audit). The tutorial keeps plaintext guest fields so the host dashboard stays searchable; operational GDPR lives in user-data-rights-demo where that story is the point of the app.

Guest fields are declared plaintext so the host dashboard can search and sort the list without a KMS adapter:

name: createTextField({
required: true,
searchable: true,
sortable: true,
allowPlaintext: "guest-list search/sort, no KMS provisioned",
}),
email: createTextField({
searchable: true,
allowPlaintext: "guest-list search, no KMS provisioned",
}),
— from src/features/show-pony/schema/rsvp.ts

Production bootstrap opts in explicitly:

allowPlaintextPii: "show-pony demo app, no KMS provisioned",
— from bin/main.ts

Why: encrypted PII fields cannot be indexed for the guest-list screen. A real product either accepts that limitation, stores a hashed lookup key, or keeps display copies outside the encrypted column — Show Pony keeps the tutorial path simple.

Chapter 13 mounts composePagesStack — public HTML from seeded text-content blocks. That satisfies transparency (Impressum, Datenschutzerklärung) but does not register export/forget jobs.

Operational GDPR lives in bundled features:

  • compliance-profiles — region profile (e.g. eu-dsgvo)
  • data-retention — retention policies per entity
  • user-data-rights — Art. 15/17/18/20 pipeline
  • user-data-rights-defaults — stock hooks for user + file refs

The runnable reference app is user-data-rights-demo — todos + files + export ZIP + forget cron. Wire it with composeGdprStack() from @cosmicdrift/kumiko-dev-server/compose-stacks (see that app’s run-config.ts).

  1. KMScreatePgKmsAdapter(...) (or in-memory in dev) so PII columns can use pii: true instead of allowPlaintext.
  2. composeGdprStack() + user-data-rights in APP_FEATURES.
  3. r.useExtension(EXT_USER_DATA, "rsvp", { export, delete }) — two hooks per entity; the framework runs export ZIP + forget cleanup (see user-data-rights recipe).
  4. Process — guest erasure may be email-based (no user account); hosts need an admin action or magic-link flow — design that in your domain, not in the tutorial.

Show Pony stops at (0): honest plaintext demo + legal text. Copy the GDPR stack from the sample when Mira goes live.

Back to the tutorial index: Show Pony.