Loading post…
Loading Detroit Meets…
Loading post…
Same deal as April: every month I publish a plain-language log of what changed on the data and platform side — what we collect, what we removed, who we rely on, and any government or legal request we received (this month: none). If a number or a third party shows up that should not be there, you will see it here first.
May was the v4.0 and v4.1 month — Live Activities, a web inbox, MapLibre on the map (with a written tile-provider commitment), optional cross-device sync without accounts, and Instagram paste-to-prefill with server-side vision. The Privacy Policy got a material update on May 21; this report is the companion piece.
*.basemaps.cartocdn.com — not Mapbox, not Google Maps tiles. We committed to that provider in writing here and in Section 9 of the Privacy Policy.push_device) and per-activity update tokens (on live_activity_session). Optional drive-minutes echo to our server (about once per five minutes while a Live Activity is refreshing location, plus an immediate one-shot when iOS infers you are likely en route — integers only, no coordinates). The Privacy Policy still says “once per minute”; that is stale relative to the iOS code and should be corrected there.location. Used only while a Live Activity is active, under When In Use permission — we do not request “Always” location. Raw GPS does not leave the device except as the drive-minutes integer described above./api/cron/nightly-prune). Read timestamps (readAt) are stored server-side per installation id when you mark a row read.POST /api/submit/from-url — server fetches a URL you paste (Instagram posts get image download + optional vision prefill via Vercel AI Gateway). Not on the public browse render path; only runs when you explicitly ask for prefill./api/address-suggest has existed since April; May 21 wired it into the public submit form (it was already on the organizer admin meet form). Queries are proxied to OSM Nominatim. This is not the April runtime geocoding loop on map/home; that path stays deleted.This is the month in ship order — useful if you are diffing the repo against last month’s report.
| Date | Ship |
|---|---|
| May 6 | 3.2 — entry-fee UI, timezone hints, meet-card layout; staff permissions + audit log detail; Privacy Policy update for permission flags and internal staff notes (4c9b5f5, 782dad6). |
| May 11 | Security / reliability audit (tiers 0–7) — sync payload cap, anon sync rate limits, /api/push/unregister token check, timing-safe cron bearers, staff password minimum 20, baseline CSP, structured reportError logging to our host only (b1b8fc1, 7345e81 CSP img-src https:). |
| May 14 | v4.0 — MapLibre map, service-worker offline caches, inbox + live-activity schema, /status + /api/health, submit from-url (non-AI), iOS Share Extension + widgets + Live Activities + leave-by scheduler, site announcement (d4b2e12, 14c30d1, 90042bc Detroit GP Community Pick UI, c847326 inbox mark-read, 9782ecd removed alternate app icons after brief experiment). |
| May 21 | 4.1 — Instagram vision prefill (Vercel AI Gateway), address suggest on public submit, cross-device sync ID link, /submit/status, Near me sort, web footer + PWA manifest, featured-blog push dedupe on device (70ae79d, b2066a0, c865931). |
Everything from the April report still applies unless noted below. There is still no anonymous telemetry beacon and no third-party analytics SDK.
Notification inbox rows (web + linked installs). When we send you a push or need an in-app record of it, we may write a row keyed to your installation identifier: kind (meet_change, meet_request_status, blog_featured, meet_status, etc.), title, body, deep link, optional meet id, read timestamp. These power /inbox and the Menu badge; mark-read landed May 14 (c847326). Rows older than 90 days are purged nightly. Schema: src/server/db/schema/anon-sync.ts (anon_notification_inbox).
Meet submission status lookup. At /submit/status you can enter the same contact detail you used on the submit form and/or your sync ID. We return only rows that match what you typed. Anyone who knows your contact detail or sync ID could run the same lookup — treat them like lightweight credentials.
Anonymous submit drafts (cloud mirror). When sync is enabled, in-progress submit form JSON can mirror to anon_user_pref.submitDraftJson keyed by installation id so a linked device can resume. Only fields you have entered are stored; nothing is published until staff approves a submission.
Live Activity session state. For iOS installs that register push-to-start tokens, we store: installation id, meet id, activity update token, timestamps for start/end/last update, and optionally last drive-minutes (integer) from the on-device echo endpoint. Used to push phase changes and end activities cleanly. Schema: src/server/db/schema/live-activity.ts.
Site announcements. Admins can publish a plain-text sitewide notice in Postgres (singleton row). Dismiss state is stored only in your browser’s local storage (which updatedAt you dismissed), not keyed to installation id on the server.
Staff-only (organizer accounts). May 6 policy update: permission flags and optional internal staff notes on user rows — visible only to site administrators, never on public meet pages. No change to the public visitor data model.
v4.0 briefly shipped alternate icon assets; commit 9782ecd (May 14) removed the picker and plugin the same day. End state: one icon, no extra data surface.
April’s deletion of runtime Nominatim geocoding on map/home is still in force. May did not bring that back.
The full-screen map and home map widget now use MapLibre GL instead of Leaflet. Basemap tiles are unchanged: light Voyager and dark Dark Matter styles from CARTO (basemaps.cartocdn.com), built from OpenStreetMap data. Your browser or app still fetches tiles directly from CARTO when you pan and zoom; we do not proxy tiles through our origin.
Offline / cache story: the production service worker may cache recent tiles and a bounded manifest of saved-meet ids on your device (user-configurable budget in Menu). Cache stays on-device; no account or tracking identifiers in those buckets.
Health checks probe the same CARTO style URL we ship in production (src/server/health/probes.ts).
/status (May 14, d4b2e12)/inbox for installs with sync enabled./submit with a prefill query; the same /api/submit/from-url path runs as manual paste (see 4.1 for vision).LeaveByNotificationScheduler); server crons can send supplemental tiered alerts when a Live Activity session exists./status page plus JSON /api/health — aggregate probes (DB, CARTO style URL, etc.); no per-user payloads. system_heartbeat rows support cron success/failure bookkeeping.0027_site_announcement) — singleton admin-edited message; dismiss/reopen stored per install in local storage.recommended flag).POST /api/submit/from-url (first cut) — OG/Twitter metadata parse and Instagram image mirror; vision not required yet.0018_rate_limit_bucket) — Postgres-backed buckets when RATE_LIMIT_DURABLE=true; /api/submit/from-url uses them today. Sync and inbox writes still use per-isolate in-memory limits (60/min/IP).dispatch-live-activities, dispatch-live-activity-updates, nightly-prune.70ae79d)POST /api/submit/from-url (vision layer) — rate-limited to 12 requests per minute per IP. Fetches up to 256 KB of HTML for generic URLs (512 KB for Instagram image paths), User-Agent: DetMeetsShareBot/1.0, no cookies forwarded. For instagram.com posts/reels: downloads the first post image, mirrors a copy to Catbox for moderator review, and when AI_GATEWAY_API_KEY is set, runs vision prefill (caption + image → structured draft fields via Vercel AI Gateway). Fetched HTML is not retained server-side; only parsed values return to your form. /api/admin/meet-prefill-from-url reuses the same server modules for staff meet forms.meet-request-status-push.ts)./submit/status — public status lookup (documented in Privacy Policy §2).b2066a0 shared util with map browse).manifest.webmanifest for add-to-home-screen; dismiss state in local storage only (detmeets.pwa-install-dismissed.v1).c865931); server dispatch uses uncached blog reads post-deploy./api/address-suggest (since April 2) proxies typed queries to Nominatim server-side (normalized text only, 40 requests/minute/IP in-memory limit, 90s server cache). In May it is used on the public submit form and the organizer admin meet editor — not on map render, not on home render. Organizer publish-time geocoding (one-shot, stored in our DB) was already disclosed; that behavior continues.
NSSupportsLiveActivities and NSSupportsLiveActivitiesFrequentUpdates enabled.UIBackgroundModes → location for Live Activity drive-time refresh (bounded as in Privacy Policy §9).Permissions-driven staff controls, richer meet audit logs, internal notes on user rows, Instagram prefill for admin meet forms (/api/admin/meet-prefill-from-url) — same fetch/vision patterns as public submit but staff-gated.
Full list as of May 31, 2026. Render path = what loads when a typical visitor browses meets, map, or detail without submitting a form or opting into extra features.
On the render path (unchanged from April unless noted):
detmeets.com.*.basemaps.cartocdn.com. Direct tile fetch from your device; we do not pass installation or sync ids to CARTO.Off the render path — only when you take a specific action:
/api/address-suggest when you type in submit or the admin meet form; one-shot geocode at organizer publish; not on map/home page load.instagram.com) — our server fetches public post HTML/images when you use paste-from-link prefill (your IP is not sent to Meta; our server IP is).openai/gpt-5.4 in code). No vision calls on browse/map/home.We do not use:
If we receive any of the above in a future month, the next report will say so. If we are ever gagged from saying so, the canary block above will simply not be repeated.
b1b8fc1) — capped anonymous sync batches at 500 saved entries per POST; 60 sync writes / minute / IP (in-memory per isolate); /api/push/unregister verifies the APNs token matches the stored row when you send a token (otherwise a tight per-IP cap applies); timing-safe bearer comparison on cron secrets; newly generated staff passwords are 20 characters (generateStaffPassword); Better Auth still allows user-chosen passwords from 12 characters up; idempotent meet-request state transitions; pooled APNs HTTP/2 client and transient-failure retry telemetry for push dispatch (still no third-party error SaaS).0018) — durable enforcement is wired for /api/submit/from-url when RATE_LIMIT_DURABLE=true. Address suggest uses a separate in-memory limiter, not the durable table.CRON_SECRET bearer; May 14 hardened error handling around cron/APNs failures (b9cbef2).7345e81) — img-src https: so meet covers and mirrored Instagram images render without per-host enumeration drift.reportError / reportEvent emit JSON lines to Vercel function logs only; not shipped to Sentry or any vendor.If you find a security issue, message @alex.30mm on Instagram.
0011–0027; 0010_meet_requests landed April 30). Breakdown: May 6 permissions + internal notes (0011–0017); May 14 rate limits, inbox, live activity, submit draft + suggested cover, leave-by alert flags, heartbeat, announcement (0018–0027)./api/submit/from-url (May 14, vision May 21), /api/admin/meet-prefill-from-url (May 21), /api/push/live-activity/*, /api/cron/nightly-prune, /api/cron/dispatch-live-activities, /api/cron/dispatch-live-activity-updates, /api/health; public pages /inbox (May 14), /submit/status (May 21), /status (May 14). /api/address-suggest predates May (April 2); May 21 refactored its Nominatim backend and wired it into public submit.4.0 in d4b2e12 (May 14); build 21 at month end.April’s report promised we’d name the map tile provider and disclose the next wave of platform changes. May delivered both: CARTO + OSM, via MapLibre, plus the Live Activity and submit-prefill machinery — all documented in the Privacy Policy and here.
If something is wrong, missing, or you want a category tracked next month, message @alex.30mm on Instagram.
Effective: May 31, 2026 · Period covered: May 1 – May 31, 2026
Privacy Policy · Terms · April 2026 Transparency Report · Detroit Meets on the App Store