# Evidence capture — ttfestival.nl (full site)

Forensic, timestamped capture of the official **TT Festival** website
(`ttfestival.nl`), collected for the TT Festival privacy investigation. This is
the public-facing event site that the app-promotion chain
(`gemeente Assen → app.ttfestival.nl → Appmiral`) sits on top of.

This snapshot has two parts:

1. **A complete site mirror** — every page in the site's own `sitemap_index.xml`
   (**174/174 pages**) plus all page assets, captured with GNU Wget. Because the
   mirror is large (~1.16 GB, mostly WordPress image size-variants), the bytes
   live in the companion **private** repo
   [`GraafG/tt-festival-app-evidence`](https://github.com/GraafG/tt-festival-app-evidence).
   This folder records the SHA-256 of every mirrored file in
   [`mirror-manifest.sha256`](mirror-manifest.sha256) and the page-by-page
   coverage proof in [`meta/coverage-report.txt`](meta/coverage-report.txt).
2. **Light forensic artifacts for the key pages** (kept here, public) — exact raw
   server responses, JavaScript-rendered DOM, full-page screenshots and response
   headers for the five pages most relevant to the investigation.

## Snapshot summary

| Field | Value |
|-------|-------|
| Captured (local) | **2026-06-26 23:42 CEST (UTC+02:00)** |
| Mirror started (UTC) | **2026-06-26 22:24:23Z** (see `logs/capture-start.txt`) |
| Capture host | Microsoft Windows 11 Pro |
| Sitemap pages | **174** (`sitemap_index.xml` → 6 sub-sitemaps) |
| Mirror coverage | **174 / 174** (8 emoji-slug pages curl-recovered — see below) |
| Mirror size | ~1.16 GB / ~6,100 files (in the private companion repo) |
| Key pages with raw+DOM+screenshots | 5 |
| Server | Apache + WordPress (`/wp-json/` REST API advertised) |
| User-Agent used | `Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/126.0.0.0 Safari/537.36` |

## Key pages captured (raw + rendered DOM + screenshots, here in the public repo)

| # | URL | What it is | HTTP | Notes |
|---|-----|-----------|------|-------|
| 1 | <https://ttfestival.nl/> | Home | 200 | WordPress; advertises `wp-json` REST API. |
| 2 | <https://ttfestival.nl/programma/> → `/artiesten/` | Programme / line-up | **301 → 200** | `/programma/` 301-redirects to `/artiesten/` (`X-Redirect-By: redirection`). DOM/screenshot are of the resolved `/artiesten/` page. |
| 3 | <https://ttfestival.nl/info/huisregels/> | House rules | 200 | Consent-by-entry to image/audio recording (see below). |
| 4 | App announcement post | 200 | "Download nu de TT Festival App 👇 **app.ttfestival.nl**" — the entry point of the app redirect chain. |
| 5 | <https://ttfestival.nl/info/faq/> | FAQ | 200 | Practical info / frequently asked questions. |

## Why this is evidence

1. **House rules: entering the festival = automatic consent to image/audio
   recording, used for "openbare orde" *and* promotion.** `info/huisregels/`
   states verbatim:

   > "Tijdens het TT Festival kunnen beeld- en geluidsopnamen worden gemaakt
   > waarop jij zichtbaar bent."
   > "…opnamen kunnen worden gebruikt in het kader van handhaving van de openbare
   > orde en veiligheid, maar ook voor promotionele doeleinden."
   > "Door het terrein te betreden geef je toestemming voor het gebruik van
   > dergelijk beeld- en geluidsmateriaal."

   Consent is presented as automatic on entry, and the same footage is used both
   for public-order enforcement and for marketing — relevant to how the event and
   its app treat attendees' personal data.

2. **The site is the on-ramp to the app tracking chain.** The app-announcement
   page sends the public to **`app.ttfestival.nl`** ("Download nu de TT Festival
   App"). That host is the exact start of the redirect chain captured in
   [`../appmiral.com_2026-06-26_232241/`](../appmiral.com_2026-06-26_232241/)
   (`app.ttfestival.nl → ttfestival2026.deeplink.appmiral.com → Appmiral/AWS`),
   tying the public event site to the third-party data processing documented
   elsewhere in this repo.

3. **Full-site fixed point.** Capturing the whole site (174/174 sitemap pages)
   preserves the surrounding context — programme, practical info, news posts — as
   it stood on the capture date, so individual quotes can't later be dismissed as
   cherry-picked or be quietly edited away.

## Full-mirror coverage & the 8 recovered pages

The site's `sitemap_index.xml` lists **174 pages**; all 174 are present in the
mirror. Eight of them have slugs containing emoji or other multi-byte characters
(e.g. *"Nieuwe TT Festival app 📱"*). **GNU Wget on Windows cannot write those
filenames**, so it skipped them; each is live (HTTP 200) and was recovered
directly with `curl` into `mirror/ttfestival.nl/_recovered/` under a short, stable
filename, with a `filename → original-URL` map in
`mirror/ttfestival.nl/_recovered/RECOVERED-PAGES.txt` (private repo). The full
audit — including which pages wget saved as `slug.html` vs `slug/index.html` — is
in [`meta/coverage-report.txt`](meta/coverage-report.txt).

## Folder layout

```
ttfestival.nl_2026-06-26_234242/
├── README.md                 ← this capture log
├── SHA256SUMS.txt            ← SHA-256 of every file in THIS (public) folder
├── mirror-manifest.sha256    ← SHA-256 of every file in the full site mirror,
│                               which lives in the private companion repo
├── .capture-timestamp
├── raw/                      ← EXACT unmodified server responses (curl GET)
│   ├── home.html / .headers.txt
│   ├── programma.html / .headers.txt        (the 301 + resolved 200 headers)
│   ├── info-huisregels.html / .headers.txt
│   ├── app-announcement.html / .headers.txt
│   └── info-faq.html / .headers.txt
├── rendered-dom/             ← DOM after JS hydration (Chrome)
│   ├── home.html
│   ├── programma-artiesten.html
│   ├── info-huisregels.html
│   ├── app-announcement.html
│   └── info-faq.html
├── screenshots/              ← full-page + viewport PNGs (1440-wide desktop)
│   ├── 01_home_ttfestival.nl_*            04_app-announcement_*
│   ├── 02_programma-artiesten_*           05_info-faq_*
│   └── 03_info-huisregels_*
├── meta/                     ← the sitemaps + the verifiable coverage proof
│   ├── sitemap_index.xml + 6 sub-sitemaps
│   ├── sitemap-urls.txt      ← the 174 page URLs (seed list for the mirror)
│   └── coverage-report.txt   ← 174/174 coverage audit + the 8 recovered pages
├── logs/
│   ├── capture-start.txt
│   └── tool-versions.txt
└── mirror/  → MOVED to private repo GraafG/tt-festival-app-evidence
               (full wget --mirror clone of all 174 pages + assets, ~1.16 GB;
                wget-command.txt + wget-mirror.log live there too. Integrity of
                every mirrored file is recorded above in mirror-manifest.sha256.)
```

## Verify integrity

```bash
sha256sum -c SHA256SUMS.txt          # this public folder, run inside it
```

```powershell
Get-Content SHA256SUMS.txt | ForEach-Object { $h,$p = $_ -split '  ',2
  if ((Get-FileHash -Algorithm SHA256 -LiteralPath $p).Hash.ToLower() -ne $h) { "MISMATCH: $p" } }
```

To verify the full mirror, clone the private companion repo and run
`sha256sum -c SHA256SUMS.txt` inside its `ttfestival.nl_2026-06-26_234242/`
folder; that list is byte-identical to this folder's `mirror-manifest.sha256`.

## Reproduce

- **Visual:** open any PNG in `screenshots/`.
- **Exact bytes:** files in `raw/` are the verbatim server responses (the
  `programma` headers show the live `301 → /artiesten/` redirect).
- **Rendered state:** files in `rendered-dom/` are the DOM after JavaScript ran.
- **Whole site:** clone
  [`GraafG/tt-festival-app-evidence`](https://github.com/GraafG/tt-festival-app-evidence)
  and open `ttfestival.nl_2026-06-26_234242/mirror/ttfestival.nl/index.html`.
- **Re-mirror:** the exact wget command is in the private repo's
  `ttfestival.nl_2026-06-26_234242/wget-command.txt`; it is seeded from
  `meta/sitemap-urls.txt` (preserved here).

> These captures mirror **public** pages for fixed-point, reproducible reference.
> URLs are preserved unmodified.
