Thithi-Yoga-Karanam-led matrimony site: architecture and roadmap

A matrimony site where the Guru's chart-matching method ranks and explains every match, built on Cloudflare with all matching logic on the server.

Summary Contents

We are building a matrimony site where the Guru's chart-matching method ranks and explains every match, running entirely on Cloudflare with all matching logic on the server.

The reference site (mayyam.in) supplies the general shape: register, build a profile, search with filters, view a profile, express interest. Our difference is that birth data is a required input, charts are computed by our backend, and search results are ordered by compatibility for the logged-in member.

Ground rules for the team

  • Own members only. We reuse the common profile field list and the general flow. We do not copy the reference site's member data, photos, text, branding or code.
  • Server decides everything. Chart calculation, match scoring, tier gating and field redaction all happen in Workers. The browser only renders what it is given.
  • Privacy by default. Guests see a minimal card. Contact details, exact birth data and family details are released only by tier and by mutual interest.
  • One URL per state. Search filters live in the query string and every profile has a server-rendered page, so pages are shareable and indexable where we allow it.

Architecture Contents

Everything runs on Cloudflare: one front-end Worker for pages, one API Worker for data and logic, D1 for records, R2 for photos. The front end never talks to D1 or R2 directly.

Browser Web Worker API Worker D1 R2 KV Queue Match Worker server-rendered pages auth, search, profiles users, profiles, charts photos, horoscopes sessions, lookups charts and scores
The browser only ever reaches the Web Worker and the API Worker. Chart and score computation happens off the request path in the Match Worker, which writes results back to D1.
ComponentCloudflare productJob
Web front endWorkers (Next.js via OpenNext, or Astro)Server-rendered pages: home, search, profile, account
APIWorkers (Hono router)/api/v1/*: auth, profiles, search, interests, admin
DatabaseD1 (SQLite)All relational data, accessed through Drizzle ORM with migrations
FilesR2 + signed URLsOriginal photos kept private; blurred and watermarked variants generated on upload
Sessions and lookupsKVSession tokens, cached dropdown lists (castes, stars, districts)
Background workQueues + Match WorkerCompute chart on profile save; recompute match scores
Bot protectionTurnstile + WAF rate limitsRegister, login, OTP and search endpoints
Email and SMSExternal provider (e.g. Resend, MSG91)OTP, interest notifications
PaymentsRazorpay (webhook into API Worker)Plan purchase and renewal

On D1. It is a good starting point: cheap, zero-ops, and fine for tens of thousands of profiles. Its limits are a 10 GB cap per database, a single write region, and no full-text or geo search. Keep all SQL behind a repository layer so a later move to Postgres (through Hyperdrive) touches one module. Do not put queries in route handlers.

On the chart library. Swiss Ephemeris is the standard for planetary positions and compiles to WebAssembly, which Workers can run. It is dual-licensed (AGPL or a paid licence), so decide on the licence before using it in a closed-source product. If the WASM bundle proves too heavy for a Worker, the fallback is a small container service behind the Match Worker; nothing else in the design changes.

Data model Contents

The profile is split across tables by sensitivity, so a query for a guest card cannot accidentally select contact or birth data.

Fields the team expects to add or remove later go in a JSON extras column rather than new migrations.

TableHoldsSensitivity
usersid, email, phone, password hash, role (member, staff, admin), status, created_atPrivate
sessionstoken hash, user_id, expires_at, user agentPrivate
otp_codesphone or email, code hash, purpose, attempts, expires_atPrivate
profilesid, user_id, handle, display name, gender, dob, height, weight, marital status, mother tongue, body type, complexion, physical status, habits, about text, status, plan, extras JSONCard fields public; dob members only
profile_religionreligion, caste, sub-caste, gothram, kulam temple, star, raasi, doshamMembers only
profile_professionaleducation, specialization, institution, sector, industry, job title, income bandMembers only
profile_locationcountry, state, district, city, addressCity public; address paid only
profile_familyparents' names and status, family type, values, status, siblings, house, land, assetsPaid or mutual interest
profile_contactsrelationship, mobile, emailPaid or mutual interest
profile_photosR2 keys for original, blurred and watermarked variants; sort order; approval statusVariant depends on tier
birth_databirth date, time, place, latitude, longitude, timezone, time accuracyPrivate; never sent to clients
chartsprofile_id, method version, computed placements as JSON, computed_atSummary fields members only
partner_preferencesage and height ranges, marital status, habits, religion, caste, star, dosham, education, income, locationMembers only
match_scoresprofile_a, profile_b, method version, score, breakdown JSON, computed_atOwn matches only
interactionsfrom, to, type (view, shortlist, interest, contact request), state, created_atOwn rows only
saved_searchesuser_id, name, filter JSONOwn rows only
plans, paymentsplan definitions; Razorpay order and payment ids, status, periodPrivate
lookupstype (caste, star, education, district...), id, label per language, parent_idPublic
audit_logactor, action, target, timestampStaff only

Indexes that search depends on: profiles(status, gender, dob), profile_religion(caste, star), profile_location(state, district), match_scores(profile_a, score).

Profile status values: REGISTERED (account only), PENDING (submitted, awaiting approval), ACTIVE, HOLD, HIDDEN, CLOSED. Only ACTIVE profiles ever appear in search or on public URLs.

Registration and login Contents

Registration is a short account step followed by a guided profile wizard, and a profile goes live only after staff approval.

Phone OTP is the primary identity check because most members will register from a phone, often a parent on behalf of the bride or groom.

  1. Sign up name, phone, email, password, who the profile is for
  2. Verify phone by OTP
  3. Wizard 1: basic details
  4. Wizard 2: birth date, time and place
  5. Wizard 3: religion, education, work, family
  6. Wizard 4: photos and partner preferences
  7. Status PENDING: staff review approve, or send back to the member with a reason
  8. Status ACTIVE chart computed, first match scores queued
Each wizard step saves on its own, so a member who drops off can resume. Approval triggers the chart computation and the first batch of match scores.

Registration rules

  • Turnstile on the sign-up form; rate limit OTP sends to 3 per phone per hour and 5 verify attempts per code.
  • Members must be of legal marriage age (21 for men, 18 for women in India today); check at sign-up from the date of birth.
  • "Profile created for" (self, son, daughter, sibling, relative) is recorded, plus explicit consent that the person described agrees to the listing.
  • Handle is generated from the display name plus a short random suffix, not a running number, so handles cannot be enumerated.
  • Birth time has an accuracy field (exact, approximate, unknown). The matching engine needs to know how to treat the last two.

Login

  • Phone or email plus password, or phone plus OTP. Google sign-in is optional and can wait for a later phase.
  • Passwords hashed with Argon2id or PBKDF2 through the Web Crypto API (bcrypt is slow on Workers).
  • On success the API Worker sets an HttpOnly, Secure, SameSite=Lax session cookie holding a random token. Only a hash of the token is stored. Sessions last 30 days and slide on use.
  • Every API request resolves the session to {user_id, role, plan} in middleware. Handlers receive that context and never read the cookie themselves.
  • Lock the account for 15 minutes after 10 failed attempts. Password reset is by OTP to the verified phone.
EndpointPurpose
POST /api/v1/auth/registerCreate account, send OTP
POST /api/v1/auth/otp/verifyVerify phone or email
POST /api/v1/auth/loginPassword or OTP login, sets session cookie
POST /api/v1/auth/logoutDelete session
POST /api/v1/auth/password/resetOTP-based reset
GET /api/v1/meCurrent user, role, plan, profile completion

Profiles and visibility Contents

One serializer in the API Worker decides which fields a viewer receives, and every endpoint that returns a profile goes through it.

The front end shows a "register to view" or "upgrade to view" prompt wherever a section arrives empty.

SectionGuestFree memberPaid memberMutual interest accepted
Card: first name, age, height, city, education, occupationYesYesYesYes
PhotosBlurred, first onlyWatermarkedWatermarked, allAll
Religion, caste, star, raasi, doshamNoYesYesYes
Compatibility score with meNoScore onlyScore and breakdownScore and breakdown
Professional detail, income bandNoYesYesYes
Family detailsNoNoYesYes
Chart (raasi and navamsam view)NoNoYesYes
Full name, mobile, email, addressNoNoLimited by plan quotaYes
Exact date, time and place of birthNoNoNoOnly if the owner opts in

The exact tiers are a business decision for the Guru; the table is a starting proposal. Whatever is chosen, the rule is that a field a viewer may not see is absent from the JSON, not hidden by the page.

Profile page. GET /p/{handle} is server-rendered by the Web Worker, which calls GET /api/v1/profiles/{handle} with the viewer's session. Sections mirror the data model: about, basic details, religion and horoscope, professional, location, family, lifestyle, partner preferences, contact. Non-active profiles return 404 to everyone except the owner and staff.

Actions on a profile

ActionEndpointNotes
Record a viewautomatic on profile fetchOne row per viewer per day
ShortlistPOST /api/v1/profiles/{handle}/shortlistPrivate to the member
Send interestPOST /api/v1/profiles/{handle}/interestRecipient can accept or decline; daily cap by plan
Respond to interestPOST /api/v1/interests/{id}/respondAccepting unlocks the mutual-interest column
Request contactPOST /api/v1/profiles/{handle}/contactDeducts from plan quota; logged
Report or blockPOST /api/v1/profiles/{handle}/reportGoes to the staff queue

Editing. Members edit their own profile section by section through PATCH /api/v1/me/profile/{section}. Changes to name, about text and photos go back through approval; other fields apply immediately. Any change to birth data queues a chart recompute.

Charts and the matching engine Contents

The Guru's method lives in one server-side module with a fixed interface, so the rest of the site can be built before the rules are final.

Nothing about the rules, weights or intermediate values is shipped to the browser; clients receive a score and an explanation.

  1. Birth data saved or profile approved
  2. Queue
  3. Compute chart ephemeris + ayanamsa
  4. Store in charts, pick candidates gender, age, preferences
  5. Guru rule engine scores each pair
  6. Store in match_scores
Charts are computed once per profile and reused. Scores are computed per pair in the background, never during a search request.

Module interface

FunctionInputOutput
computeChartbirth date, time, place coordinates, timezone, time accuracylagna, planet placements by sign and house, nakshatra and pada, navamsa, dasha periods, doshams, plus the method_version
scoreMatchtwo charts, optional profile facts (age gap, preferences)score 0 to 100, verdict (recommended, acceptable, not recommended), breakdown as a list of {rule, result, points, note}
explainbreakdown, languagemember-readable text in English or Tamil

Design rules

  • Rules are data where possible: a versioned rules table or JSON (rule id, weight, condition) that the Guru's team can adjust through the admin screen without a code deploy. Rules that need real logic stay in code.
  • Every chart and score stores the method_version that produced it. When the rules change, bump the version and let the queue recompute in the background; old scores stay visible until replaced.
  • Candidate selection keeps the work bounded. For a new profile, score only against active profiles of the other gender within the preference age band, typically a few thousand pairs, processed in batches of 100 per queue message.
  • Scores are symmetric unless the method says otherwise; store one row per pair with the lower id first.
  • Approximate or unknown birth time: the engine must state which rules it skipped, and the explanation must say the result is partial.
  • Include a manual override: the Guru or staff can pin a verdict and note on a pair, which takes precedence over the computed one.
  • Build a test suite from 20 to 30 pairs the Guru has already judged by hand. The engine is not released until it reproduces his verdicts on them.

Endpoints

EndpointReturns
GET /api/v1/me/matches?page=1The member's best matches by score
GET /api/v1/profiles/{handle}/matchScore and, by tier, the breakdown against the logged-in member
GET /api/v1/profiles/{handle}/chartRendered chart data for display (paid tier)
POST /api/v1/admin/method/recomputeQueue a recompute for a version (admin only)

Admin, moderation and payments Contents

Staff tools are a separate app on their own hostname (for example admin.), protected by Cloudflare Access, so no staff screens or staff-only fields are ever bundled into the member site.

  • Approval queue. New and edited profiles and photos wait here. Staff approve, reject with a reason, or put a profile on hold.
  • Member support. Look up a member, view payment history, reset access, create a profile on behalf of a walk-in customer.
  • Reports. Review reported profiles; block or close accounts.
  • Method console. Edit rule weights, publish a new method version, trigger recompute, pin manual verdicts.
  • Lookups. Maintain castes, sub-castes, education and location lists in English and Tamil.
  • Audit log. Every staff action that reads contact details or changes a profile is recorded.

Payments. Razorpay Checkout on the plan page. The API Worker creates the order, and a signed webhook (POST /api/v1/webhooks/razorpay) is the only thing that activates a plan. Plans define duration, contact-view quota and daily interest cap. Offline payments at the Guru's office are entered by staff and flow through the same plan activation code.

Security and privacy Contents

A matrimony site holds birth details, caste, income, family and phone numbers, so the API must be safe even if someone calls it directly without the website.

These are the requirements to review each endpoint against.

  • Redact on the server. Responses contain only what the viewer's tier allows. No internal fields (staff names, follow-up notes, IP addresses, internal ids, non-active statuses) in any member-facing response.
  • Photos. Originals live in a private R2 bucket. Clients get short-lived signed URLs to the variant their tier allows. Blurring is done at upload, not with CSS.
  • Input. Validate every request body and query with a schema (Zod). Bound parameters only. Plain ids in, never serialized objects.
  • Abuse. Turnstile on public forms; WAF rate limits on auth, OTP, search and profile views; daily caps on interests and contact views; random handles so profiles cannot be walked in sequence.
  • Sessions. HttpOnly cookies, CSRF token or same-site checks on state-changing requests, staff behind Cloudflare Access with 2FA.
  • Secrets. Worker secrets for API keys; separate D1 databases and R2 buckets for dev, staging and production; no production data in lower environments.
  • India's DPDP Act. Clear consent notice at sign-up, purpose limitation, a way to download and delete one's data, a named grievance contact, and breach notification duties. Get this reviewed by a lawyer before launch.
  • Deletion. Closing an account removes photos and contact data and anonymises the rest after a fixed retention period.
  • Backups. D1 Time Travel covers 30 days; add a weekly export to R2.

Roadmap Contents

Six phases take the site from empty repository to public launch in about 18 weeks for a team of two or three developers.

The durations are estimates to be revised once the team and the Guru's rule set are known.

PhaseWeeksDeliversDone when
0. Foundations1 to 2Repos, Workers projects, D1 migrations, dev/staging/prod environments, CI deploys, design system, lookup dataA hello-world page and API deploy to staging on every merge
1. Accounts and profiles3 to 5Register, OTP, login, profile wizard, photo upload with variants, profile page, visibility serializer, approval queueA tester can register, be approved and view another profile with correct redaction at each tier
2. Search6 to 8Search endpoint, filter UI with URL sync, defaults, pagination, saved searches, lookups APISearch with and without values matches the spec; guest limits enforced
3. Charts and matching7 to 11Birth data capture with geocoding, chart computation, rule engine v1, background scoring, match sort, explanation textEngine reproduces the Guru's verdicts on the hand-judged test pairs
4. Interaction and payments10 to 13Shortlist, interests, contact requests with quotas, notifications, Razorpay plans, staff support toolsA paid member can complete interest, acceptance and contact reveal end to end
5. Hardening and launch14 to 18Tamil language, security review, load test, DPDP review, backups, analytics, seeded launch with the Guru's existing clientsExternal security review passed; first 100 consented real profiles live
6. After launchongoingMobile app or PWA, community sub-sites, method console improvements, move to Postgres if D1 limits are nearDriven by usage

Phases 2 and 3 overlap on purpose: search does not depend on the engine, and the engine work can start as soon as the Guru's rules are written down.

Team

  • One full-stack developer on the Web Worker and UI.
  • One backend developer on the API, D1 and queues.
  • One person pairing with the Guru to turn his method into written rules and test pairs; this is the critical path for phase 3.
  • Part-time design and QA; staff from the Guru's office to run the approval queue from phase 1 testing onward.

Open questions Contents

These need answers before the affected phase starts; the first three block the matching engine design.

  • What does the Guru's method take as input and produce: star-based porutham with his own weights, full chart analysis with dashas and planetary positions, or a manual review per pair?
  • Which ayanamsa and chart style does he use (Lahiri, KP, other; South Indian chart layout)?
  • How should profiles with approximate or unknown birth time be handled: partial score, excluded from match sort, or referred to him?
  • Which communities and languages at launch: one community in Tamil and English, or several?
  • Tiers and pricing: what does a free member see, what do paid plans unlock, and are there offline payments at his office?
  • Does he have existing clients to invite at launch, and have they consented to being listed online?
  • Swiss Ephemeris licence: AGPL (open-source our engine) or buy the commercial licence?
  • Front-end framework: Next.js on Workers or Astro? Decide in phase 0 based on the team's experience.
  • Who on his staff will run approvals and support, and in what hours?
  • Domain name and brand, and whether the site sits alongside the existing Cloudflare sites or in a separate account.