From 3c37895969e6d331d27735657505736c579db56e Mon Sep 17 00:00:00 2001 From: SimpleTest Date: Tue, 21 Jul 2026 21:29:41 +0300 Subject: [PATCH] Improve map fallback and verification evidence --- assets/css/app.css | 102 +++++++++++++++++++++++++++++++++++++++++++ assets/js/hooks.js | 47 ++++++++++++++++++-- docs/verification.md | 45 ++++++++++++++----- 3 files changed, 181 insertions(+), 13 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index 8639619..d08ba93 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -176,6 +176,108 @@ display: none; } +.home-demo-map-fallback { + position: relative; + display: grid; + min-height: inherit; + align-content: end; + padding: 5.75rem 1.25rem 1.25rem; + background: + linear-gradient(32deg, transparent 47%, color-mix(in oklab, var(--color-base-content) 14%, transparent) 48% 51%, transparent 52%), + linear-gradient(148deg, transparent 42%, color-mix(in oklab, var(--color-base-content) 10%, transparent) 43% 46%, transparent 47%), + radial-gradient(circle at 28% 22%, color-mix(in oklab, var(--color-success) 18%, transparent) 0 12%, transparent 13%), + radial-gradient(circle at 78% 38%, color-mix(in oklab, var(--color-error) 14%, transparent) 0 10%, transparent 11%), + color-mix(in oklab, var(--color-base-200) 86%, var(--color-success) 14%); +} + +.home-demo-map-fallback::before { + position: absolute; + inset: 0; + content: ""; + opacity: 0.28; + background-image: + linear-gradient(color-mix(in oklab, var(--color-base-content) 16%, transparent) 1px, transparent 1px), + linear-gradient(90deg, color-mix(in oklab, var(--color-base-content) 16%, transparent) 1px, transparent 1px); + background-size: 3.25rem 3.25rem; +} + +.home-demo-map-fallback-note { + position: relative; + z-index: 1; + max-width: 32rem; + margin: 0 0 0.75rem; + padding: 0.625rem 0.875rem; + border: 1px solid color-mix(in oklab, var(--color-base-content) 12%, transparent); + border-radius: 1rem; + background: color-mix(in oklab, var(--color-base-100) 94%, transparent); + color: color-mix(in oklab, var(--color-base-content) 72%, transparent); + font-size: 0.75rem; + line-height: 1.35; + box-shadow: 0 0.5rem 1.5rem rgb(23 37 31 / 0.08); + backdrop-filter: blur(0.5rem); +} + +.home-demo-map-fallback-points { + position: relative; + z-index: 1; + display: grid; + gap: 0.625rem; +} + +.home-demo-map-fallback-point { + display: flex; + align-items: center; + gap: 0.75rem; + width: min(100%, 23rem); + padding: 0.625rem 0.75rem; + border: 1px solid color-mix(in oklab, var(--color-base-content) 12%, transparent); + border-radius: 1rem; + background: color-mix(in oklab, var(--color-base-100) 96%, transparent); + box-shadow: 0 0.75rem 2rem rgb(23 37 31 / 0.11); + backdrop-filter: blur(0.5rem); +} + +.home-demo-map-fallback-point:nth-child(2) { + justify-self: end; +} + +.home-demo-map-fallback-marker { + display: grid; + width: 2.25rem; + height: 2.25rem; + flex: 0 0 auto; + place-items: center; + border: 0.2rem solid var(--color-base-100); + border-radius: 9999px; + background: var(--color-success); + color: var(--color-success-content); + font-size: 0.75rem; + font-weight: 900; + box-shadow: 0 0.25rem 0.75rem rgb(23 37 31 / 0.18); +} + +.home-demo-map-fallback-copy { + display: grid; + min-width: 0; + gap: 0.125rem; +} + +.home-demo-map-fallback-copy strong, +.home-demo-map-fallback-copy small { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.home-demo-map-fallback-copy strong { + font-size: 0.8125rem; +} + +.home-demo-map-fallback-copy small { + color: color-mix(in oklab, var(--color-base-content) 62%, transparent); + font-size: 0.6875rem; +} + @keyframes home-map-spin { to { transform: translate(-50%, -50%) rotate(360deg); diff --git a/assets/js/hooks.js b/assets/js/hooks.js index 47befd8..10d8fb1 100644 --- a/assets/js/hooks.js +++ b/assets/js/hooks.js @@ -51,10 +51,51 @@ const createAidMap = element => { state.element.dataset.mapUnavailable = "true" state.markReady() - const fallback = document.createElement("p") - fallback.className = "grid h-full place-items-center p-6 text-center text-sm" + if (state.element.dataset.demoMap !== "true") { + const fallback = document.createElement("p") + fallback.className = "grid h-full place-items-center p-6 text-center text-sm" + fallback.dataset.mapFallback = "true" + fallback.textContent = state.element.dataset.mapUnavailableLabel + state.element.replaceChildren(fallback) + return + } + + const fallback = document.createElement("div") + fallback.className = "home-demo-map-fallback" fallback.dataset.mapFallback = "true" - fallback.textContent = state.element.dataset.mapUnavailableLabel + + const note = document.createElement("p") + note.className = "home-demo-map-fallback-note" + note.textContent = state.element.dataset.mapUnavailableLabel + fallback.append(note) + + const points = document.createElement("div") + points.className = "home-demo-map-fallback-points" + + markerPoints(state.element).forEach((point, index) => { + const item = document.createElement("div") + item.className = "home-demo-map-fallback-point" + + const marker = document.createElement("span") + marker.className = "home-demo-map-fallback-marker" + marker.setAttribute("aria-hidden", "true") + marker.textContent = String(index + 1) + + const copy = document.createElement("span") + copy.className = "home-demo-map-fallback-copy" + + const title = document.createElement("strong") + title.textContent = point.title || "" + + const location = document.createElement("small") + location.textContent = point.location || "" + + copy.append(title, location) + item.append(marker, copy) + points.append(item) + }) + + fallback.append(points) state.element.replaceChildren(fallback) } diff --git a/docs/verification.md b/docs/verification.md index 9633b3f..d2062c4 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -16,7 +16,7 @@ results from product limits and unknown production properties. | Consent-driven live tracking | Implemented and cross-client verified | On API 37, Android started `TrackingService` as a location foreground service with a persistent Stop notification. After Home minimized the Activity, an emulator coordinate change reached PostGIS. Notification Stop removed the service, notification, active session, and raw position. | Browsers stop with the page. Android has no `ACCESS_BACKGROUND_LOCATION`, unattended start, or route history. | | Privacy settings | Implemented and browser-verified | The profile exposed hidden, approximate public, exact for active match, and explicit exact-public options. Blocking and current-position cleanup have automated tests. | Exact public location remains a user opt-in; legal privacy and retention text still requires jurisdiction-specific review before launch. | | Reputation and anti-abuse | Implemented at MVP level | Handover codes, two-party completion, double-blind reviews, unique-counterpart ranking, optional movement/proximity evidence, reports, blocks, abuse signals, and moderator audit paths have automated tests. | The system is not bot-proof and does not claim identity verification. No punitive numeric policy is enabled without measured and approved thresholds. | -| Account registration and sign-in | Implemented and browser-verified | Email registration sends a confirmation magic link and does not require a password. Confirmed users can keep using magic links or add a password in settings. Google OpenID Connect registration, sign-in, link, unlink, replay prevention, verified-email enforcement, and account-ownership rules are covered by the 260-test suite. A headed Chrome run completed registration, confirmation through Mailpit, password setup, logout, password login, and localized settings with zero console warnings or errors. | The local Compose environment captures email in Mailpit. A production SMTP relay and a real Google Web client are not configured or externally verified. | +| Account registration and sign-in | Implemented and browser-verified | Email registration sends a confirmation magic link and does not require a password. Confirmed users can keep using magic links or add a password in settings. Google OpenID Connect registration, sign-in, link, unlink, replay prevention, verified-email enforcement, and account-ownership rules are covered by the 285-test suite. A headed Chrome run against the public test domain created a new account through the real Google provider, stored one confirmed/terms-accepted user and one Google identity, logged out, and logged back in without a second completion step or duplicate row. The same account then completed the isolated Mailpit magic-link flow; the one-time login token was consumed and only a session token remained. | Test email is deliberately captured in its own Mailpit. A production UniSender delivery-format message reached Gmail, but a real production authentication email and the production Google callback remain unexercised until the tested release is explicitly promoted. | | Social profiles | Manual links implemented; optional GitHub verification implemented and automated-tested | Manual links cannot set verification fields. The optional GitHub flow uses state, PKCE, a user-bound one-time session, unique provider ownership, and an audit record. The local protocol drill also performs real HTTP token/user exchanges without returning an access token to the application. | GitHub OAuth credentials are intentionally absent and are not required for registration or the help flow. The real external provider redirect/callback remains disabled and unverified; other providers remain manual/unverified. | | Support and content removal | Implemented and browser-verified | Public support, account deletion, general removal, and TAKE IT DOWN forms create separate audited queues; private email status links verify public contacts; authenticated submissions use the account email; moderator-only operations can update status and notify verified contacts. TAKE IT DOWN accepts URLs/text only and records a 48-hour review due time. | The current product hosts no user media and does not claim TAKE IT DOWN coverage. Staffing, jurisdiction-specific legal classification, final retention rules, actual account erasure/export, and identical-media-copy handling remain operational/legal work. | | Voluntary thanks | Implemented as an external optional link | A helper can expose an optional link after completion; the UI states that the platform does not process the payment. | The platform does not provide payments, escrow, refunds, tax reporting, or payment guarantees. | @@ -24,10 +24,31 @@ results from product limits and unknown production properties. | Multiple web/worker instances | Implemented and locally failure/rollout-verified | The final isolated Compose drill passed BEAM crashes and sequential replacement with 3 web/2 worker replicas: all five nodes joined, PubSub passed, and 744/744 readiness requests succeeded. The project-owned kind cluster replaced all 2 web/2 worker pod UIDs under `maxUnavailable=0`; all four replacement pods joined, PubSub passed, and 363/363 samples ultimately succeeded. | Local PostGIS is a single instance. Production database HA, backups, and recovery are operator work and are not claimed complete. | | Local observability | Implemented and protocol-verified | Pinned Prometheus scraped the exact 3 web and 2 worker targets with a file Bearer credential; Grafana provisioned a healthy datasource and ten-panel web/worker/BEAM/Ecto/Oban dashboard; Alertmanager delivered firing and resolved webhooks for an induced scoped replica stop. | Local delivery does not establish production retention, notification-provider reliability, on-call policy, or measured alert thresholds. | | Encrypted local backup | Implemented and failure-verified | Pinned Restic streamed PostgreSQL custom format into pinned local MinIO with no host plaintext dump, passed full-data checking and a fresh-database restore, rejected a corrupted repository, and published no snapshot for an interrupted upload. The one-run MinIO project and volume were removed after retaining the non-secret evidence. | The drill proves the local mechanism, not off-site durability, database HA, or a production RPO/RTO/retention policy. | -| External protocol boundaries | Implemented and locally failure-verified | The production release used its configured Assent/Req and Swoosh/gen_smtp clients against internal-only mocks. GitHub OAuth, Google OIDC discovery/authorization/token/JWKS with nonce and PKCE, and SMTP success/rejection/retry/replay/timeout paths passed. The HTTP push boundary passed disabled, retry, rejection, timeout, and idempotency paths. Request acceptance and new-chat transactions created durable jobs processed by two Oban worker replicas; the chat event completed on Oban attempt 2 after an injected temporary failure. | This does not verify real external provider availability or device delivery. Google/GitHub production clients, production SMTP, FCM/APNs token registration, and provider selection remain external work; SMTP exactly-once delivery is not claimed. | +| External protocol boundaries | Implemented and locally failure-verified | The production release used its configured Assent/Req and Swoosh/gen_smtp clients against internal-only mocks. GitHub OAuth, Google OIDC discovery/authorization/token/JWKS with nonce and PKCE, and SMTP success/rejection/retry/replay/timeout paths passed. The HTTP push boundary passed disabled, retry, rejection, timeout, and idempotency paths. Request acceptance and new-chat transactions created durable jobs processed by two Oban worker replicas; the chat event completed on Oban attempt 2 after an injected temporary failure. A separate public test-domain run exercised the real Google OIDC provider, and a production UniSender Go delivery-format message reached Gmail. | The real GitHub provider, the production Google callback, production authentication-email delivery, FCM/APNs token registration, and device delivery remain unverified. SMTP exactly-once delivery is not claimed. | ## Reproducible checks +- On 2026-07-21, commit `811ddf4c5c3847fbb0c2861e6e72fcd6a9bd91fe` + was deployed only to `https://test.whoneedhelp.com`. Headed Chrome attached to + the user's existing dev-port profile completed real Google authorization, + one-time account creation, logout, and returning-user Google login. Browser + console inspection reported zero errors and zero warnings for the app flow. + Read-only PostgreSQL checks observed exactly `1 user / 1 Google identity / 0 + duplicate provider UIDs`; the user was confirmed, had accepted terms, and + had no password. A subsequent email magic-link was delivered to the isolated + test Mailpit, required an explicit confirmation POST, signed the same user + in, and left `0` reusable `login` tokens and `1` active `session` token. + Application logs recorded only the expected 200/302 responses for those + paths. Production remained on commit + `4f2a9aaacc8eca606f3287df5c8663be49f9595b` during this verification. +- A separate non-authentication production delivery-format check was accepted + by UniSender Go and observed in Gmail from + `Who Need Help `. The intended HTTPS test-domain + link remained the link's actual destination because link tracking was + disabled. UniSender appended its sender attribution and an unsubscribe link + on `email.whoneedhelp.com`; this provider-added footer was observed rather + than inferred. No account or application database row was created by that + delivery check. - On 2026-07-21, `./scripts/test.sh` and the Dockerized `mix precommit` each passed 273 tests after the support/content-removal implementation. The full isolated `./scripts/quality.sh` gate passed compiler, xref, Credo, Sobelow, @@ -50,10 +71,11 @@ results from product limits and unknown production properties. checks. It passed together with GitHub OAuth, SMTP, push, and two-worker Oban paths. Evidence is retained at `output/external-boundaries/google-auth-fixed-20260720`. -- Headed Chrome verified the public temporary HTTPS origin through the +- In an earlier isolated local run, headed Chrome verified the temporary HTTPS + origin through the email-only registration form, Mailpit confirmation link, one-time login, password creation, logout, password login, Russian locale selection, and - Google connection settings. The configured Google credential pair is empty, + Google connection settings. That run's Google credential pair was empty, so the UI correctly left Google actions disabled. Browser console inspection reported zero errors and zero warnings. The run-owned account and its one cascading login token were removed after read-only relationship checks; no @@ -1138,9 +1160,11 @@ availability, or target-server capacity. ## Known work before a public production launch -- Replace the temporary staging origin with the production-owned domain and - production infrastructure. The temporary origin is available only while the - workstation, Compose stack, VPN path, gateway, and their networks are up. +- Promote the tested release from `test.whoneedhelp.com` to the independent + production project only after explicit approval. Recheck the production + health endpoints, migrations, Google callback, and authentication-email flow + after that promotion; the current test origin still depends on its configured + workstation/VPN/gateway path. - Confirm the final Android application ID before creating its Play Console listing, publish `/.well-known/assetlinks.json` for that ID and the final signing fingerprint if verified App Links are wanted, and complete store @@ -1151,9 +1175,10 @@ availability, or target-server capacity. - After provider approval, verify that delivered MIME contains neither open nor link tracking and omits the unsubscribe block, then exercise registration and magic-link delivery through the deployed application to a real mailbox. -- Deploy the already configured production and staging Google OAuth clients, - then exercise registration, sign-in, and settings linking against Google on - their exact HTTPS callback origins. +- Exercise registration, sign-in, and settings linking against the production + Google OAuth client on its exact HTTPS callback origin after the tested + release is explicitly promoted. The test client and callback have already + completed real registration and returning-user login. - Configure and verify a real mobile push provider and device-token lifecycle if native push is required. The provider-neutral HTTP boundary and product jobs are tested; FCM/APNs device delivery is not.