Repository navigation
Description
The goal
Everything a visitor sends us — signing up for a class, or reporting a problem with a listing — goes through one safe, consistent path, and we can show that it works.
Why it matters
Each kind of submission used to have its own path, with its own gaps in spam protection and its own way of reaching a person. Both atlas forms have been on the one shared path for weeks, and every piece of work filed under this goal is closed. Since the last check, two more of the things that were missing have landed: the live site now runs the real spam check rather than the test setting that let everything through, and both forms now explain a refusal in the same words instead of ending in a bare "something went wrong".
Two things are still outstanding. Nobody has sent a sign-up or a problem report from the live site and watched it arrive — until someone does, we are reading the code rather than watching it work. And since 3 November the spam check only loads on partner sites that are on our list of allowed sites, so the forms on partner sites that are not listed yet, the WordPress ones included, turn every visitor away. sydevs/SahajCloud#1077 fixes that, and this goal waits on it.
The digest of wrongly refused sign-ups, agreed on 21 October, is now built as part of sydevs/SahajCloud#1070, alongside every other manager email, rather than here.
What success looks like
- Both atlas forms — signing up for a class, and reporting a problem — arrive in the one submissions list, each marked with what kind it is.
- A visitor can report a problem using an ordinary email address, including a Gmail, Yahoo or Outlook one. (Repaired on 11 October and live since.)
- The spam check on the live site is the real one, not the test setting that accepts everything, and it cannot quietly revert to that setting. If the test setting ever reaches the live site, both forms stop and an error is recorded, rather than everything passing unnoticed. (Real since 3 November.)
- A test sign-up and a test problem report, both sent using an ordinary email address, are accepted and arrive within fifteen minutes — the sign-up as a confirmation email, the report in the general contact inbox. Both are sent from sahajatlas.com and from a partner site that shows the map inside its own page.
- When either form turns a visitor away, it tells them what to change, in the same words for both forms. No submission ends in a bare "something went wrong".
Not included
- New kinds of forms. Adding a class or suggesting a correction from the map belongs to Anyone can submit a class or suggest a correction, safely #257, not here.
- Offering visitors a mailing-list opt-in when they sign up for a class. The receiving side is ready for it, but asking for that consent is a new question on the form, and needs its own wording.
- Replacing how spam is detected. This goal is about every form being checked the same way, not about changing the checks.
- Confirming that a message we accepted was also delivered. Accepting a submission and delivering it are separate steps, and delivery reliability is its own goal.
- The digest of refused sign-ups. Agreed here on 21 October, it is built by feat(atlas): open-items homes, per-role digests, simpler preferences SahajCloud#1070 under Every Atlas manager gets a digest and a home shaped for their role SahajCloud#1067, as the "Refused sign-ups" notification preference beside every other manager email. Building it here would duplicate that work.
Decisions
- Where should a problem report about the map land? → The general contact inbox. Nothing to set up; corrections share it with every other message. (Ardnived, 2026-10-21)
- What should happen when the spam check wrongly refuses a real class sign-up? → "Notify admins in a digest email, make it configurable via the notifications preferences like all other notifications." Every refusal reaches admins in a digest. Delivered by feat(atlas): open-items homes, per-role digests, simpler preferences SahajCloud#1070, not this goal. (Ardnived, 2026-10-21; moved 2026-11-05)
- How do we keep the live site's spam check from quietly reverting to the test setting? → Accept what guards it now: if the live site ever carries the test setting, it serves no spam check at all, so both forms stop and an error is recorded. No release check is added. This replaces the 21 October answer, whose release check was removed when the spam check moved onto our own servers. (Ardnived, 2026-11-05)
Open questions
None.
Notes
Twelve of the thirteen pieces of work under this goal are closed. The open one is sydevs/SahajCloud#1077, attached on 2026-11-05: partner sites missing from the allowed-sites list cannot take a sign-up or a report. The production spam-check secret was read on 2026-11-05 and is not the test one (Ardnived).
Related: #195, #214, #216, #257, #343, sydevs/SahajCloud#695, sydevs/SahajCloud#723, sydevs/SahajCloud#724, sydevs/SahajCloud#725, sydevs/SahajCloud#796, sydevs/SahajCloud#797, sydevs/SahajCloud#798, sydevs/SahajCloud#799, sydevs/SahajCloud#800, sydevs/SahajCloud#814, sydevs/SahajCloud#654, sydevs/SahajCloud#899, sydevs/SahajCloud#1072, sydevs/WeMeditateWeb#78, sydevs/WeMeditateWeb#116
Technical notes
TL;DR
- Settled: production serves the real Turnstile sitekey
0x4AAAAAAEFpp8n3BBZfG0KF, from SahajCloud's own/api/atlas/challengeframe (read live 2026-11-05); both forms now map every refusal code from one shared table. - Risky: fix(atlas): list every live embed host in allowedDomains, which the captcha now requires SahajCloud#1077 —
/api/atlas/challengerenders only for a parent on a published client'sallowedDomains(or first-party/loopback), and production data was never checked against that, so inline embeds on unlisted partner hosts show the captcha-blocked screen. - Unknown: no submission has ever been sent from the live site. Production's
TURNSTILE_SECRET_KEYwas read by Ardnived on 2026-11-05 and is not the test secret; whether it pairs with the served sitekey is what the criterion 4 test settles. - Decided 2026-11-05: the runtime 503 is the accepted guard (old question 1, answer A). The refusal digest moved to feat(atlas): open-items homes, per-role digests, simpler preferences SahajCloud#1070.
Verification, criterion by criterion (2026-11-05)
| Criterion | Verdict | Evidence |
|---|---|---|
| 1. Both forms in one list, each typed | met | createRegistration sends type: 'registration', sendReport sends type: 'contact', both to /user-submissions (mutate.ts) |
| 2. An ordinary email address is accepted | met | URL_PATTERN run over 12 providers: 0 refused, was 8. Live since 11 Oct (SahajCloud#899, #922). A link in a message body is still refused |
| 3. Real spam check live, cannot quietly revert | met (runtime guard accepted 2026-11-05) | Live GET /api/atlas/challenge → 200 carrying 0x4AAAAAAEFpp8n3BBZfG0KF; a test key in production → 503 — see the next block |
| 4. A test sign-up and report arrive within 15 min | not done | Needs a real browser to pass Turnstile. GET /api/user-submissions → 403: an API client holds create-only, by design. The partner-site half waits on sydevs/SahajCloud#1077 |
| 5. Both forms say what to change | met | REFUSAL_MESSAGE_KEYS is a total Record over each synced union, sharing INPUT_REFUSAL_KEYS with the report form; event_not_found mapped (RegistrationForm.tsx:75, PR #345). A new code fails the build rather than degrading |
| moved | To sydevs/SahajCloud#1070 § Preferences (refused_signups), which keeps PR #1052's registration-refusals collection and screening write |
What changed since the 2026-10-24 check
| Landed | What it did to this goal |
|---|---|
| SahajAtlasWeb#345 | Criterion 5. Both forms refuse in one vocabulary, from ReportIssueForm/refusal-copy.ts. submission_data_invalid is excluded on purpose: no registration this widget sends can earn it (mutate.ts:69) |
| SahajAtlasWeb#346 | Criterion 3's release half: a Vite plugin refused a production build carrying a test key, plus a smoke assertion |
| SahajAtlasWeb#343 → PR #355, with SahajCloud#1072 | Moved the whole challenge into a frame SahajCloud serves, so one hostname covers every partner page and one deployment holds both halves of the credential. VITE_TURNSTILE_SITE_KEY no longer exists in this repo, and #346's guard and smoke assertion went with it |
#343 is a Bug, so it stays direct and is not attached as a child. docs/embedding.md already carries the new frame-src cloud.sydevelopers.com row and its failure mode, so the host contract is current.
The release guard became a runtime refusal (question 1)
atlasChallenge returns 503 and logs production carries a Turnstile TEST key when isProductionDeployment() && isTestSiteKey(siteKey) (SahajCloud/src/endpoints/atlas/challenge/index.ts:81, helper at challengeRequest.ts:56, which names this goal). The widget then shows its security-check error and takes no submission.
So the test key can no longer be a silent no-op — the failure is loud. Nothing fails a release; Ardnived accepted that on 2026-11-05. No smoke spec reads the live sitekey any more (#355 removed #346's), so the 503's error log is the only signal.
What is left
| Item | Who |
|---|---|
sydevs/SahajCloud#1077 — list every live inline-embed host in allowedDomains |
the loop (child, High) |
| Criterion 4 — one sign-up and one report from sahajatlas.com and from an inline partner embed, Gmail sender | a person with a real browser; the partner half after #1077 |
No SahajAtlasWeb code remains. The refusal digest is sydevs/SahajCloud#1070's.
Facts settled, so they are not asked
| Question | Answer |
|---|---|
| Is the report path switched on in production? | Yes — form 3 "Report Issue Form": two required fields, textarea:message and email:email |
| Who receives a report? | Unreadable client-side by design (SahajCloud#813). A blank recipient falls back to contact@sydevelopers.com |
| Does a host see a change? | Yes, and it is already documented: frame-src https://cloud.sydevelopers.com is required, and challenges.cloudflare.com now belongs in no host directive (docs/embedding.md) |
| Who may frame the challenge? | Only a published client's allowedDomains, a first-party frontend, or loopback; the page posts to exactly that origin |
| URL contract touched? | No — atlas-url-contract.json names no endpoint, form or submission |
| Honeypot or edge rate limiting? | No honeypot in either repo; rate limiting is a Cloudflare dashboard rule the code only documents |
Build vs buy, and priority
- Nothing to buy. The form-builder plugin is the buy, and is why this goal is mostly done. Replacing the screening and delivery stack is out of scope — its most valuable part, cross-intake sender history keyed on this table, no third party supplies.
- Priority stays Medium for the goal; its live-breakage child fix(atlas): list every live embed host in allowedDomains, which the captcha now requires SahajCloud#1077 is High.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Projects
- StatusShow more project fieldsAwaiting
Activity
sydevs-bot commentedon Oct 8, 2026
🤖 no session running
recent dispatches
Written by the sydevs dispatcher. Sessions never edit this comment.
34 remaining items
sydevs-bot commentedon Nov 5, 2026
The session for revise-roadmap did not finish after 3 attempts. I have stopped retrying. A
@sydevs-botverb or mention from you starts me again.🤖 sydevs dispatcher
sydevs-bot commentedon Nov 5, 2026
Not yet — three of six lines are met. Two of the three that are not need no code.
Two more landed since the last check: the live site now runs the real spam check, not the test setting — #343 moved it onto our own servers on 3 November — and both forms now tell a visitor what to change instead of a bare error (#345).
What is left:
@sydevs-bot revise 1Ato accept that, or1Bto add the release check too.Once question 1 is answered,
@sydevs-bot implementfiles the remaining work.🤖 Written by the sydevs autonomous loop — see what this is
Generated by Claude Code
fix(api): allow the Turnstile token header through CORS preflight (#1085
fix(registration): blame the connection, not the captcha, when nothin…
fix(api): accept an issue report from an API client (#1089)