Building a Vedic Astrology Engine from First Principles
Why we're building this in the open
Most astrology software is a black box. You enter your birth data, get a result, and have no way to verify the methodology, audit the calculations, or understand why one time window scored higher than another.
We think that's backwards. Vedic Muhurta is a systematic methodology — every step can be documented, every calculation verified, every weight inspected. There's no reason to hide the engine behind a proprietary wall.
Shubh Time is open source. This post documents the engineering decisions we made and why.
The architecture
Shubh Time is a TypeScript monorepo with four applications and eight domain libraries:
Applications:
apps/api— NestJS REST API serving birth chart calculations, Muhurta window scoring, remedies, and notification preferencesapps/web— React + Vite frontend with interactive birth chart visualization (D3.js North Indian format), Dasha timeline, and remedy trackingapps/landing— Next.js 15 marketing site with waitlist captureapps/worker— Background job processor for chart pre-computation and notification delivery
Domain libraries:
libs/astro-core— Chart calculation with dual-engine architecture (Swiss Ephemeris + AstrologyAPI), Redis caching, and cross-reference validationlibs/muhurta— Pure 10-step Panchanga scoring algorithm with configurable per-event weightslibs/dasha— Vimshottari Dasha period calculation and timeline generationlibs/gochar— Gochar (transit) analysis enginelibs/compat— Compatibility scoring for joint-chart analysislibs/predict— Planetary influence analysis across life areaslibs/remedy— Rule-based remedy recommendation engine (5 classical categories)libs/notify— Notification preferences and delivery service
Decision 1: Why TypeScript, not Python
Most astronomical computation libraries are in C or Python. We chose TypeScript for three reasons:
-
One language for the full stack. The API, frontend, worker, and all domain libraries share types. A
BirthChartinterface defined inastro-coreflows from calculation through scoring to API response to D3 visualization without a single type conversion. -
Swiss Ephemeris has Node.js bindings. The
swissephnpm package wraps the C library with sub-arcsecond accuracy. We get C-level precision with TypeScript-level developer experience. -
The developer audience expects it. Our primary users are developers building astrology-powered products. TypeScript is the lingua franca of modern web development. An npm-installable SDK is more useful to this audience than a Python package.
Decision 2: Dual-engine calculation
Birth chart accuracy is non-negotiable. A degree of error can change which Nakshatra the Moon occupies, which cascades through the entire Dasha sequence and every downstream score.
We use two independent calculation engines:
Swiss Ephemeris — Sub-arcsecond astronomical computation. Runs locally with no network dependency. The gold standard for planetary position calculation.
AstrologyAPI — Independent third-party calculation service. Provides a cross-reference validation source with its own ephemeris data.
The chart calculation service uses a circuit breaker pattern (via opossum): if the primary engine fails or its circuit opens, the fallback engine handles the request. Both engines use the Lahiri ayanamsa (sidereal zodiac), consistent with the Parashari tradition.
Chart results are cached in Redis (SLO: <20ms cache hit) and persisted in PostgreSQL. Fresh calculations target <500ms.
Decision 3: The 10-step scoring pipeline
We didn't invent the Muhurta scoring methodology — it's derived from classical texts (Muhurta Chintamani, Brihat Samhita). But we did have to decide how to encode it computationally.
The 10 steps evaluate:
| Step | Classical Concept | What It Assesses | |------|------------------|-----------------| | 1 | Tithi | Lunar day quality for the event type | | 2 | Vara | Weekday suitability | | 3 | Nakshatra | Lunar mansion quality | | 4 | Yoga | Sun+Moon combined position | | 5 | Karana | Half-Tithi unit | | 6 | Panchaka | Nakshatra avoidance rules | | 7 | Rahu Kala | Inauspicious period exclusion (hard block) | | 8 | Transit strength | Event lord's current planetary dignity | | 9 | Lagna | Rising sign quality for the event | | 10 | Chandra Bala | Moon strength relative to natal Moon |
These fold into 6 weighted score buckets (max 100 total):
- Panchaanga (20 pts) — Steps 1-5
- Nakshatra Fit (15 pts) — Step 3 bonus + Step 6
- Transit Strength (20 pts) — Steps 7-8
- Dasha Harmony (20 pts) — Step 10 + Paksha bonus
- Yogas (15 pts) — Special Nakshatra-Vara combinations (Guru Pushya, Sarvartha Siddhi, etc.)
- Lagna Strength (10 pts) — Step 9
The scoring algorithm (libs/muhurta/src/muhurta-algorithm.ts) is a pure function — no database calls, no side effects, no NestJS dependencies. Same inputs always produce the same score. This makes it testable, auditable, and portable.
Decision 4: Configurable event weights
Not all events are created equal in Muhurta. A wedding prioritizes different factors than a business launch. Rather than hardcoding these differences, we made the weight configuration a first-class data structure.
Each event type (marriage, business_launch, career, travel, medical_procedure, education_start) has its own EventWeightConfig specifying:
- Which Tithis are auspicious or inauspicious
- Which Nakshatras are favorable
- Which Varas (weekdays) are preferred
- The event lord planet (Venus for marriage, Mercury for business, Sun for career)
- Preferred Lagna modality (fixed for marriage, movable for business)
- Whether Panchaka rules are strict
The admin panel includes weight sliders for real-time tuning. This means practitioners can adjust the engine to match their regional traditions or specific school of interpretation.
Decision 5: Phase 1 scope
We launched with six event types: marriage, business_launch, career, travel, medical_procedure, and education_start. Any other event type returns a 422 error with a clear message.
Why limit at all? Because each event type requires its own validated weight configuration, and we'd rather ship six event types with thoroughly tested weights than twelve with guessed ones. The Muhurta algorithm itself is event-agnostic — adding new event types is a data problem, not a code problem.
Decision 6: Rule-based remedies
The remedy engine (libs/remedy/) is deliberately rule-based, not ML-based. It analyzes the user's active Mahadasha lord and checks for planetary debilitations, then recommends from five classical categories:
- Ratna (gemstones) — e.g., Yellow Sapphire for Jupiter
- Mantra — e.g., Mahamrityunjaya for Moon
- Yantra — e.g., Sri Yantra for Venus
- Ritual — e.g., Surya Arghya for Sun
- Dana (charitable acts) — e.g., blue cloth donation for Saturn
Priority is boosted when the Dasha lord is debilitated. Results are lazily computed and cached in the database, recomputing only when the Dasha period transitions.
We chose rule-based over ML because:
- The remedy tradition is well-documented — there's no ambiguity to "learn" from data
- Users can inspect exactly why a remedy was recommended
- Practitioners can verify recommendations against their own training
Decision 7: 48-minute windows
Each Muhurta window is exactly 48 minutes — 2 ghatikas, the classical Muhurta unit of time. We considered offering configurable window sizes but decided against it for Phase 1. The classical unit is well-understood by practitioners, and using it signals that we respect the tradition rather than abstracting it away.
What's next
We launched with six event types, one ayanamsa, and rule-based remedies. The roadmap includes:
- Additional event types (property, spiritual, and more)
- Compatibility scoring for joint-chart analysis
- Transit analysis with personalized influence periods
- Expanded ayanamsa support (Raman, KP) based on practitioner feedback
- Webhook notifications for upcoming favorable windows
We're looking for Jyotish practitioners to serve as design partners — people who will test our calculations against their own methods and tell us where we're wrong. If that's you, reach out.
Shubh Time is open source. The scoring algorithm, event weights, and engine orchestration are all inspectable in the repository.
Join the waitlist
Shubh Time is in private beta. Get early access to auspicious timing for the moments that matter.
Join waitlist