Privacy
What this site collects, what it does not collect, and how long feedback submissions live before they expire automatically.
Informational only. This page describes data handling for this site and is not legal advice.
Overview
This site is a bilingual green card preparation guide. It is designed to collect as little data as reasonably possible while still letting a single maintainer read user feedback when users choose to send it. There is no user account system, no advertising or marketing tracking pixel, no advertising partner, and no marketing automation. Operational observability may come from Cloudflare Workers logs, Cloudflare Web Analytics when enabled, and Sentry when configured. The paragraphs below describe every data surface in plain language, in the order they appear in the product.
Cloudflare access logs
The site is deployed on Cloudflare Workers. Cloudflare's platform records standard access logs (such as IP address, timestamp, requested URL, and user agent) as part of delivering the site. These logs are held by Cloudflare under Cloudflare's own policies and are not under this project's independent control. This is the same platform baseline that applies to any site hosted on Cloudflare and is unchanged from the deploy posture recorded in the repository.
How feedback submissions are handled
If you choose to submit feedback through the feedback form, the submission is validated against a stable schema and then written to a Cloudflare KV namespace bound to this project as `FEEDBACK_KV`. The submission lives inside Cloudflare Workers on the same account that serves the site. No third-party feedback collector receives a copy. The submission contents are whatever you typed into the feedback form; the form does not attach your email address or browser fingerprint, and your IP address is not stored alongside the submission. To prevent abuse, the submission endpoint applies a per-IP rate limit (up to 5 submissions per hour); the limit is enforced using a pseudonymous SHA-256 key derived from the IP address and kept in Cloudflare KV for at most 2 hours. The raw IP address is not stored in that rate-limit key, but a hash should not be treated as anonymous proof. The rate-limit key is not intentionally linked to the submission contents.
Feedback retention: 90 days, then automatic expiry
Every feedback submission is stored with a retention window of 90 days. This is enforced by Cloudflare KV via an `expirationTtl` set at the moment the submission is written, equal to 7,776,000 seconds (the constant `FEEDBACK_TTL_SECONDS` in `src/lib/feedback/storage.ts`). After the window closes, the entry expires automatically at the platform layer without requiring any maintenance job, cron task, or manual cleanup on our side. There is no "extended retention" tier and no opt-in to keep a submission longer.
Manual deletion on request
If you want a submission removed before the 90-day window closes, the maintainer can delete the specific entry on request. The deletion workflow is described in the maintainer runbook and is the same command path the maintainer uses to read incoming submissions. Please include enough context (approximate submission date, subject, or a quoted phrase) to let the maintainer find the specific entry.
Who reads feedback
Only the maintainer may read feedback submissions. Those entries are visible only on the maintainer-only admin surface, including `/admin/reviews`, and that surface is not part of the public product. Access to `/admin/*` and `/api/admin/*` is enforced by a fail-closed route-level auth gate using the maintainer credential. Feedback submissions are never exported to an external analytics provider, a customer relationship management system, a mailing list, or any other third party.
Checklist save-progress
When you save your checklist progress, a JSON envelope is written to your browser's localStorage under the key `greencardguide:checklist-state:v2`. The envelope contains: schema_version (an integer used for future migration), saved_at (an ISO-8601 timestamp set on your device), language (the locale selected when saving), and encoded_answers (your checklist question answers in URL-encoded form). The local save does not store your name, email address, IP address, or device identifier. The encoded_answers payload contains only your checklist question answers, but those answers can still describe case-specific facts. The localStorage entry stays on your device until you click "Start fresh" (which deletes the key) or clear your browser site data. Separately, when you open results, print, download a PDF, copy a share link, or request email delivery, those checklist answers are serialized into URLs or server requests for that feature. They may appear in browser history, Cloudflare access logs, PDF/download requests, copied links, and any place you paste or forward a checklist URL. No analytics events are fired by this feature.
No third-party embeds
There are no third-party video embeds, no social media widgets, no chat widgets, no advertising networks, and no "sign in with" buttons on any page. Content is served directly by this site. Feedback submissions are not sent to a third-party feedback collector: they remain inside Cloudflare KV on the account that hosts the site, visible only to the maintainer. Other optional runtime services are described in their own sections: Cloudflare provides hosting and observability, Sentry may receive sanitized server error reports, and Resend receives transactional checklist email payloads when email delivery is used.
Error monitoring
Server-side unhandled errors can be reported to Sentry, a third-party error monitoring service, but only when an operator has provisioned the SENTRY_DSN secret (set in production via `wrangler secret put SENTRY_DSN`; never committed to the repository). When that secret is absent or empty, the monitoring client returns null, a one-line warning is logged inside the Worker, and no error data leaves the runtime. When the secret is present, every event passes through a sanitizer before transmission: query strings are cleared, request bodies are replaced with the literal string `[redacted]`, and the headers Authorization, Cookie, Set-Cookie, and x-admin-auth-token are stripped. Route paths are retained because the route is needed to diagnose the failure. This minimizes and redacts PII where configured; it is not a promise that every possible personal detail can never reach Sentry. Client-side React rendering errors are NOT transmitted to Sentry. The Toucan client is constructed per request inside the Worker; there is no browser-side Sentry SDK loaded on any page. When the integration is active, the payload Sentry receives is the sanitized server-side error report described above, used solely for diagnosing runtime failures.
Checklist email delivery (Wave 8)
The personalized checklist can be requested by email from the checklist results page. Delivery is gated on two operator-provisioned Workers secrets: RESEND_API_KEY (the Resend transactional email API key) and the EMAIL_RATE_LIMIT_KV namespace binding. If either is missing, the `/api/checklist/email` endpoint responds with HTTP 503 and no message is sent; the form surfaces a fail-closed error and points you to the PDF download instead. When delivery is active, we send the message once and do not store your email address. Your recipient address is passed to Resend solely for the single transactional send, together with the generated checklist email HTML, plaintext fallback, and PDF attachment content. Your recipient address is not written to KV, is not intentionally logged, and is stripped from Sentry events where configured before capture. No marketing list exists. There is nothing to unsubscribe from. If you do not want a future copy, simply do not submit your email address again. To prevent abuse, the endpoint applies a per-IP rate limit of 3 sends per 24-hour window. The limit is enforced using a pseudonymous SHA-256 key derived from your IP address and kept in the EMAIL_RATE_LIMIT_KV namespace for up to 48 hours via Cloudflare KV `expirationTtl`. The raw IP address is not stored in that rate-limit key, but a hash should not be treated as anonymous proof. The rate-limit key is not intentionally linked to your email address.
How to get in touch
The feedback form is the preferred channel because it routes directly into the maintainer's intake. If you would rather not use the form, issues and questions can also be raised through the repository's public issue tracker, which is listed in the About page. Either channel is acceptable; neither is used for marketing communication.