diff --git a/site/README.md b/site/README.md index 03bd99c..5e193bf 100644 --- a/site/README.md +++ b/site/README.md @@ -71,13 +71,14 @@ can return Cloudflare Error 1027 until reset; existing static pages continue serving. There is no automatic upgrade or overage bill on Free. A paid plan is an optional decision if homepage/missing-route traffic exceeds the free quota. -Organization JSON-LD uses the existing feedback form. The owner's postal address -is intentionally omitted; the audit's complete Organization check will therefore -remain unmet. Set the build variable `PUBLIC_CONTACT_EMAIL` only after a dedicated -address can receive mail. No placeholder email is published. To create -`contact@tritimes.org`, provision a mailbox or configure Cloudflare Email Routing -with a verified destination inbox and the required DNS records, then verify receipt. -Email provisioning requires mail/DNS access and (for forwarding) a destination inbox. +Organization JSON-LD publishes `support@tritimes.org` alongside the existing +feedback form. Cloudflare Email Routing is enabled for `tritimes.org`, with its +MX, SPF, and DKIM records installed and an enabled exact-address forwarding rule +to the owner's verified private inbox. The destination is managed in Cloudflare; +do not publish it in this repository. Other addresses are not caught by this rule. +This is an inbound forwarding alias, not a mailbox or an outbound email service. +The owner's postal address is intentionally omitted; the audit's complete +Organization check will therefore remain unmet. ## Agent readiness @@ -90,12 +91,11 @@ The owner elected to omit a phone number and postal address, so the audit's PostalAddress recommendation is deliberately unresolved. Do not invent an address or publish an email before delivery is verified. -For a new `contact@tritimes.org` forwarding address, an administrator needs -Cloudflare Email Routing access and a verified destination inbox. Check existing -mail DNS before enabling routing; preserve any active mail provider. After the -route is active, verify delivery, then set `PUBLIC_CONTACT_EMAIL` at build time to publish it on the Contact -page and in Organization `contactPoint.email`. -No email address has been provisioned by this change. +`support@tritimes.org` is the default in `src/lib/site.ts`, used by the Contact +page and Organization `contactPoint.email`. `PUBLIC_CONTACT_EMAIL` can override +it at build time, but only use an address with a working mailbox or verified +forwarding destination. Confirm Cloudflare routing remains enabled when changing +mail DNS, and test delivery from a different sender than the destination inbox. After `npm run build`, start `npm run preview`, then run `npm run verify:readiness`. It fetches every built public file, compares served bytes to the build, parses diff --git a/site/src/lib/site.ts b/site/src/lib/site.ts index 6165647..41d205f 100644 --- a/site/src/lib/site.ts +++ b/site/src/lib/site.ts @@ -4,7 +4,7 @@ export const FEEDBACK_URL = "https://docs.google.com/forms/d/e/1FAIpQLScavYEE5U8 export const SITE_DESCRIPTION = "TriTimes is an independent triathlon results viewer for IRONMAN and IRONMAN 70.3 races, with athlete histories, full-field time distributions, and swim, bike, run, and overall percentiles."; // The operator chose not to publish a phone number or postal address. -// Add a direct email only once its mailbox or forwarding route is verified. +// Cloudflare Email Routing forwards this address to a verified private inbox. export const ORGANIZATION = { "@context": "https://schema.org", "@type": "Organization", @@ -15,6 +15,7 @@ export const ORGANIZATION = { contactPoint: { "@type": "ContactPoint", contactType: "customer support", + email: "support@tritimes.org", url: FEEDBACK_URL, }, }; diff --git a/site/src/pages/contact.astro b/site/src/pages/contact.astro index 8768c66..cf8423c 100644 --- a/site/src/pages/contact.astro +++ b/site/src/pages/contact.astro @@ -3,10 +3,11 @@ import TrustPage from "@/components/TrustPage.astro"; import { FEEDBACK_URL } from "@/lib/site"; import { organizationJsonLd } from "@/lib/organization"; const email = organizationJsonLd(import.meta.env.PUBLIC_CONTACT_EMAIL).contactPoint.email; +const description = `Contact TriTimes${email ? " by email or through its feedback form" : " through its feedback form"} to report missing races, incorrect results, site issues, or privacy concerns.`; --- - + {email &&

Email {email} to contact TriTimes.

}

Use the TriTimes feedback form to contact the project about missing races, incorrect results, athlete search problems, accessibility issues, or suggestions. This is the same feedback channel linked from the footer. The form is hosted by Google and opens outside TriTimes; Google's privacy terms apply to information you submit there.

For a results issue, include the TriTimes page URL, the race name and year, the athlete name as published, and a short description of what appears wrong. A link to the corresponding official result helps distinguish a source-data issue from a display problem. For a technical issue, describe the steps you took and the browser or device you used. Do not send passwords, payment details, or other sensitive information.

-

For a privacy question or a request concerning an athlete listing, use the same feedback form and identify the relevant page and your concern. Review the Privacy page for an explanation of published race information and local browser preferences. TriTimes is an independent project and cannot change official race records; corrections to official timing or registration information should also be raised with the event organizer. There is no guaranteed response time.

+

For a privacy question or a request concerning an athlete listing, {email ? "email TriTimes or use the feedback form" : "use the feedback form"} and identify the relevant page and your concern. Review the Privacy page for an explanation of published race information and local browser preferences. TriTimes is an independent project and cannot change official race records; corrections to official timing or registration information should also be raised with the event organizer. There is no guaranteed response time.

diff --git a/site/src/pages/privacy.astro b/site/src/pages/privacy.astro index b533711..cc55ada 100644 --- a/site/src/pages/privacy.astro +++ b/site/src/pages/privacy.astro @@ -1,9 +1,12 @@ --- import TrustPage from "@/components/TrustPage.astro"; +import { organizationJsonLd } from "@/lib/organization"; +const hasContactEmail = Boolean(organizationJsonLd(import.meta.env.PUBLIC_CONTACT_EMAIL).contactPoint.email); ---

TriTimes displays publicly published triathlon results, including athlete names, countries, age groups, race placements, and split and finish times where available. Athlete histories bring together results associated with an athlete. This information comes from the results service described in the FAQ. A public result can contain errors, and a combined history should not be treated as verified identity information.

The site does not require an account to search athletes or view results. Search matching runs in your browser after downloading result indexes. Requests for search partitions, race data, and athlete data still reach the hosting provider. TriTimes uses your browser's local storage to remember an explicitly chosen light or dark theme and races you hide from an athlete view. You can remove those preferences by clearing this site's browser storage.

Cloudflare serves the site and receives the network information needed to handle requests, such as IP addresses and requested URLs. When a Cloudflare Web Analytics token is configured for a build, the site also loads Cloudflare's analytics beacon. These services have their own data practices; see Cloudflare's privacy policy. This page does not promise a retention period for provider logs.

The feedback link leads to a Google-hosted form. Information you choose to submit is handled through that service, subject to Google's privacy policy. Avoid submitting sensitive personal information. For questions about a listing or a privacy concern, follow the instructions on the Contact page and include the relevant TriTimes URL.

+ {hasContactEmail &&

Email sent to the support address on the Contact page is routed by Cloudflare to the project's private support inbox. Your email address, message, and any attachments are processed by Cloudflare and the receiving email provider so the project can review and respond to your request. Avoid including sensitive information that is not needed to explain your concern.

}
diff --git a/site/tests/agents.test.ts b/site/tests/agents.test.ts index 8188554..9459ef3 100644 --- a/site/tests/agents.test.ts +++ b/site/tests/agents.test.ts @@ -62,15 +62,15 @@ test("Vary is case insensitive, idempotent, and preserves wildcard", () => { } }); -test("Organization uses the existing feedback contact without publishing an address or invented email", () => { +test("Organization publishes the verified support email and feedback form without a postal address", () => { const schema = organizationJsonLd(); assert.equal(schema["@type"], "Organization"); assert.equal(schema.contactPoint["@type"], "ContactPoint"); assert.ok(schema.contactPoint.contactType); assert.ok(schema.contactPoint.url.startsWith("https://docs.google.com/forms/")); assert.ok(!("address" in schema)); - assert.ok(!("email" in schema.contactPoint)); - assert.equal(organizationJsonLd("support@tritimes.org").contactPoint.email, "support@tritimes.org"); + assert.equal(schema.contactPoint.email, "support@tritimes.org"); + assert.equal(organizationJsonLd("other@tritimes.org").contactPoint.email, "other@tritimes.org"); assert.throws(() => organizationJsonLd("not an email")); });