# Staging acceptance checklist

Do not promote the application to `yourstories.life` until every critical item is checked.

## Infrastructure

- [ ] `https://staging.yourstories.life/api/health` returns `ok: true`.
- [ ] Cloudflare SSL/TLS is Full (strict).
- [ ] Direct HTTP redirects to HTTPS.
- [ ] Passenger restarts after `touch tmp/restart.txt`.
- [ ] Database, application and R2 credentials are separate from BrandPortal.
- [ ] Dynamic and authenticated routes show Cloudflare cache bypass.

## Administrator

- [ ] `ADMIN_EMAIL` receives the sign-in code.
- [ ] The email code can be used only once.
- [ ] A wrong code increments attempts and does not create a session.
- [ ] Administrator can open `/admin/qr`.
- [ ] Non-admin users cannot open `/admin/qr`.

## QR generation

- [ ] Generate one test QR.
- [ ] Export the activation CSV immediately.
- [ ] Refresh the page and confirm the activation code is no longer recoverable.
- [ ] Download PNG and SVG.
- [ ] Print the QR at the smallest intended plaque size.
- [ ] Scan it with at least two iPhones and two Android phones.
- [ ] Test under bright light, low light and at an angle.

## QR claiming

- [ ] Scanning an unclaimed QR opens `/connect/[code]`.
- [ ] The public QR alone cannot claim the record.
- [ ] A wrong activation code fails without revealing sensitive data.
- [ ] The correct activation code sends an email code.
- [ ] Verification reserves the QR and opens onboarding.
- [ ] A second unrelated user cannot claim the reserved QR.
- [ ] Abandon the flow and confirm the reservation eventually expires.
- [ ] Complete onboarding and confirm the QR becomes claimed.

## Story and privacy

- [ ] New story begins private.
- [ ] Story Keeper can edit the person, introduction, memory and dates.
- [ ] Private QR scan reveals no story content.
- [ ] Preparing public story shows only a respectful status screen.
- [ ] Published + public story opens from the same QR.
- [ ] Returning the story to private immediately stops public viewing.
- [ ] Changing the story name or slug does not require a new printed QR.

## R2 media

- [ ] JPEG upload completes directly to R2.
- [ ] PNG and WebP upload complete.
- [ ] A file over 20 MB is rejected.
- [ ] An unsupported file type is rejected.
- [ ] Browser upload works only from the allowed staging origin.
- [ ] The original object is not publicly listable or directly accessible.
- [ ] Cover photograph displays through a temporary signed address.

## Security and operations

- [ ] `ALLOW_CONSOLE_OTP=false` in production mode.
- [ ] Turnstile is required and validated server-side.
- [ ] Repeated sign-in and activation attempts are rate limited.
- [ ] Session cookie is HttpOnly, Secure and SameSite=Lax.
- [ ] Logout revokes the database session.
- [ ] Cleanup cron releases expired reservations.
- [ ] Database backup is created and restoration is tested.
- [ ] Application logs contain no activation codes, session tokens or SMTP passwords.
