Ephemeris and Astrology Computation Engine
Moira is an astronomy-first astrology engine built for transparent astrology calculations, reproducible chart computation, and an inspectable calculation chain from astronomical inputs to astrological outputs. It is an auditable astrology engine with explicit computational policy, deterministic behavior, and readable reduction stages grounded in modern standards and references including JPL DE441, IAU 2000A/2006, ERFA/SOFA-aligned practices, and Gaia DR3-linked star data where applicable. Performance-critical computations — nutation, SPK kernel reading, apparent planetary evaluation (via NativePlanetaryEvaluator), coordinate transforms, light-time iteration, harmogram analysis, and event searching — are executed by a native C++17 extension (_moira_native) compiled with pybind11.
Most astrology software surfaces results without exposing the mathematical path. Moira exists as a Swiss Ephemeris alternative for users who need visibility into assumptions, intermediates, and provenance, so astronomy remains the foundation and astrology remains the purpose.
Moira is designed to be highly discoverable and understandable by AI agents (e.g., GitHub Copilot, ChatGPT, Claude).
wiki/ directory with explicit validation reports.Moira is designed for full computational transparency: the computation pipeline is explicit and its stages are named and controllable via the Python API, computational doctrine is explicit rather than hidden in defaults, and validation is treated as first-class evidence rather than post-hoc narrative. The high-performance core (_moira_native) is C++17; the Python layer owns the API surface, orchestration, and per-stage controls.
Moira is for developers, researchers, and serious practitioners who want a programmable, audit-ready engine for high-integrity astrological work, reproducible pipelines, and methodical comparison against external authorities.
Moira is not primarily a UI app, not a thin wrapper over opaque compiled stacks, and not convenience-first astrology output generation without traceability.
Moira computes planetary and stellar positions, houses, aspects, lots, dignities, predictive techniques, a full Vedic/Jyotish suite (yogas, Shadbala, Ashtakavarga, upagrahas, avasthas, Jaimini), eclipse and occultation events, and related analytical products on top of a modern astronomical substrate (JPL kernels, IAU models, and validated star frameworks), with a native C++ computational core, Python orchestration layer, inspectable intermediate stages, and an optional FastAPI REST server (moira_server) exposing the engine as typed, versioned routes.
.bsp kernels remain supported via the integrated daf_writer for any of the 887,000+ numbered minor planets in the JPL catalog."1P/Halley") and curated short aliases accepted as inputs.house_of for direct house placement lookups.is_partile, is_platic).Every Vedic engine is implemented from primary-source research (BPHS, Brihat Jataka, Saravali, Phaladeepika, Uttara Kalamrita, Jataka Parijata, Jaimini Upadesa Sutras, and the named 1879 Agastya- and 2024 Bogamuni-attributed Pancha Pakshi witnesses) with per-rule citations; where classical sources disagree, the disagreement is an explicit policy switch or a recorded note — never a silent choice.
[0, 180) Shukla/waxing to the profile’s source-attested
Purva label and [180, 360) Krishna/waning to Amara. It accepts no location
and never selects a schedule, materializes a clock, supplies paksha to another
operation, or infers natal identity. An explicit modern local-solar context
policy can derive topocentric day/night and local-mean-solar weekday for an
aware instant and location while requiring the caller to supply Purva or
Amara. A separate modern fixed-clock policy anchors the selected schedule at
sunrise or sunset, treats one nazhigai as exactly 1,440 SI seconds on
reader-bound TT, and publishes half-open UT1 cell intervals plus their
unclipped relation to the astronomical half end. A separately named
solar-half-first selector returns the unique current fixed-clock cell under
exact half-open TT ownership, or the explicit
unmaterialized_solar_half_tail status when a long solar half outlasts the
fixed span. A distinct modern solar-proportional policy can instead preserve
the exact nominal offset fractions and map them independently across the
complete governing day or night half on reader-bound TT, returning 25
half-open TT/UT1 cells with exact astronomical-boundary closure. The 1879
witness is not credited with that proportional rule. A separate
solar-half-first selector identifies the unique proportional cell through
exact half-open TT ownership; complete-half coverage means its lawful status
is always selected, with no fixed-clock tail or fallback. Paksha remains
caller supplied on every schedule, materialization, and current-cell surface;
the standalone astronomical inference is never applied automatically, and
the 1879 profile remains explicitly non-natal. A pure Stage 2I lookup exposes
the selected 1879 generator’s first-samam EAT seed from explicit profile
Paksha, day/night half, and weekday. It does not materialize the schedule or
promote that seed into a whole-day eating bird, Padu, authority,
Adhikara/Bharana, condition, score, or forecast. All 28 cells are directly
bound to the governing 1879 leaves and separately corroborated by the 1934
Uromarisi publication; textual-lineage independence is not claimed. A
separate named Bogamuni 2024
profile preserves a complete 54-cell Purva/Amara-by-nakshatra bird table.
Its Amara verse governs because the adjacent commentary duplicates Shravana
and omits Revati; that malformed commentary remains visible as rejected
conflict evidence. The fixed modern
bogamuni_2024_apparent_lahiri_natal_moon_identity_v1 composition evaluates
apparent geocentric Sun and Moon positions on one reader-bound TT epoch,
derives the source-mapped lunar half, applies Lahiri true ayanamsa, assigns
the Moon to one of 27 equal half-open nakshatras, and returns the selected
source-table bird with every intermediate and locator visible. The source
attests nakshatra birds and phase labels; birth-Moon application, Lahiri, and
the equal-sector boundary policy are explicitly Moira-owned, not source
claims. It never routes into a schedule, current cell, score, or forecast. A
third source-scoped Bogamuni 2024 profile,
bogamuni_chennai_2024_padu_bird_mapping, preserves the separate 14-cell
Purva/Amara-by-weekday Padu-bird table. It is a pure explicit-label lookup:
the source table has no day/night axis, and its death-or-inoperative bird is
not relabelled as a schedule RULE activity, a generic authority bird, or
the separately labelled eating bird. The primary witnesses do not present an
Adhikara Pakshi table, while Bharana is secondary terminology only; neither
alias nor product is admitted. The Padu profile performs no natal identity,
astronomical routing, schedule/current-cell selection, condition scoring,
or forecast.Moira initializes even when no planetary kernel is present. Kernel-dependent operations (for example chart()) raise a clear MissingEphemerisKernelError until a kernel is configured. See Kernel Setup below before executing planetary examples.
from datetime import datetime, timezone
from moira import Moira
m = Moira()
# 1. Planetary positions
chart = m.chart(datetime(2000, 1, 1, 12, 0, tzinfo=timezone.utc))
print(f"Sun: {chart.planets['Sun'].longitude:.6f} deg")
print(f"Moon: {chart.planets['Moon'].longitude:.6f} deg")
# 2. House cusps (Placidus, London)
from moira import HouseSystem
houses = m.houses(
datetime(2000, 1, 1, 12, 0, tzinfo=timezone.utc),
latitude=51.5074,
longitude=-0.1278,
system=HouseSystem.PLACIDUS,
)
print(f"ASC: {houses.asc:.4f} deg | MC: {houses.mc:.4f} deg")
# 3. Aspect patterns
from moira.patterns import find_all_patterns
patterns = find_all_patterns(chart.longitudes())
for p in patterns:
print(f"{p.name}: {', '.join(p.bodies)}")
# 4. House placement lookup
from moira.houses import house_of
sun_house = house_of(chart.planets['Sun'].longitude, houses)
print(f"Sun is in house: {sun_house}")
The engine ships with an optional FastAPI transport layer (moira_server) that exposes the admitted engine surface as typed, versioned REST routes.
pip install moira-astro[server]
uvicorn --factory moira_server:create_app
For latency-sensitive deployments, opt in to one bounded per-worker startup warmup before accepting computational traffic:
$env:MOIRA_SERVER_PREWARM = "1"
uvicorn --factory moira_server:create_app
With prewarm enabled, /ready returns HTTP 503 until the kernel-backed warmup
succeeds; /health remains available for liveness diagnostics. Prewarm is
disabled by default because each worker pays its own native memory cost.
/v1 — charts, positions, houses, per-stage pipeline visibility, progressions (the full dispatched method menu advertised as OpenAPI enums), primary directions, returns, transits, dashas and time lords, the complete Vedic suite (yogas, shadbala, ashtakavarga, upagrahas, avasthas, Jaimini, muhurta, sade sati), draconic charts, astrocartography, asteroids and comets, fixed stars, harmonics, harmograms, electional scoring, synastry and relationship products, and more.cmake >= 3.24, and pybind11 >= 2.12 (required at build time for the native extension)# Standard install (builds the native C++ extension)
pip install moira-astro
# With the FastAPI REST server (fastapi, uvicorn, pydantic)
pip install moira-astro[server]
# With Lunar Graze support (spiceypy, laspy, requests)
pip install moira-astro[lunar-graze]
Moira requires a JPL DE-series SPK planetary kernel for all planetary computation. No kernel is bundled — the files are large and the choice of release belongs to the user.
All kernel reading is performed by Moira’s own native C++ SPK/DAF reader. As of 4.0.0 there is no jplephem runtime fallback: segment types outside the native reader’s support raise an explicit error rather than silently routing through a third-party library.
Supported kernels:
| Kernel | File | Size | Date range | Notes |
|---|---|---|---|---|
| DE441 | de441.bsp |
~3.1 GB | ~13 200 BCE – ~17 200 CE | Original design target; maximum date coverage |
| DE440 | de440.bsp |
~114 MB | 1550 BCE – 2650 CE | Current JPL standard; recommended for most users |
| DE430 | de430.bsp |
~128 MB | 1550 BCE – 2650 CE | Widely deployed predecessor to DE440 |
The easiest way to download and configure a kernel is the built-in Tkinter interface. It requires no extra dependencies — Tkinter ships with CPython on all platforms.
moira-kernel-manager
The window shows all supported kernels with extended descriptions (design rationale, date coverage, size trade-offs), live Installed/Missing status for each, and a real progress bar for downloads. You can also point Moira at a .bsp file already on disk without re-downloading.
What the GUI provides:
set_kernel_path()..bsp file already on disk and set it as the active kernel immediately.# List all kernels and their status
moira-download-kernels --list
# Download all missing kernels (interactive prompt)
moira-download-kernels
# Download without prompting
moira-download-kernels --yes
Moira supports building custom Type 13 SPK kernels using an integrated compiler GUI (built on Tkinter). This utility fetches physical position vectors directly from the JPL Horizons API and packages them into a native-readable binary kernel (.bsp).
moira-daf-writer
What the custom kernel writer provides:
Moira() succeeds even if no kernel is installed. It auto-discovers any compatible kernel in the standard locations.m.is_kernel_available() reports kernel readiness.m.get_kernel_status() explains expected paths and remediation.m.available_kernels lists installed planetary kernels (small-body shard catalogs are discovered separately via their manifests).MissingEphemerisKernelError with instructions.Standard location: kernels/<filename>.bsp relative to the repository root, or ~/.moira/kernels/. The engine resolves either automatically.
Custom location: pass the path at construction, or call set_kernel_path() before the first Moira() instantiation:
from moira.spk_reader import set_kernel_path
from moira import Moira
set_kernel_path("/path/to/de440.bsp")
m = Moira()
print(m.is_kernel_available())
print(m.get_kernel_status())
print(m.available_kernels)
Direct download links (JPL SSD):
The unified asteroid catalog (1,382 bodies as 56 Type-13 shards) and the numbered periodic comet catalog (497 comets as 20 shards) are too large to ship inside the wheel and are distributed as separate downloads. Install a catalog by placing its shard directory — asteroids/ or comets/, each containing its shards and manifest.json — under any kernel search root (kernels/ at the repository root or ~/.moira/kernels/). The engine discovers every manifest under every search root automatically; no configuration call is required.
Note for pre-4.0.0 installs: the single-file supplemental kernels (comets.bsp, centaurs.bsp, minor_bodies.bsp) no longer auto-load. All small bodies now resolve through the sharded manifests.
| Layer | Source | Bundled | Note |
|---|---|---|---|
| IAU 2000A/2006 nutation and precession tables | IAU | Yes | 2,414 terms; native C++ (_moira_native) |
| DE-series planetary kernel | JPL | No | de430 (~128 MB), de440 (~114 MB), or de441 (~3.1 GB); download separately |
| Named star registry | Sovereign (star_registry.csv + JSON provenance) |
Yes | 1,809 stars; license-independent |
| Unified asteroid catalog | JPL Horizons | No | 1,382 asteroids across all 119 families; 56 Type-13 shards, 1600–2500 CE; separate download, manifest-discovered |
| Numbered periodic comet catalog | JPL Horizons | No | 497 comets (1P–516P); 20 Type-13 shards, 1600–2500 CE; separate download, manifest-discovered |
Moira’s computational core (_moira_native) is implemented in C++17 and compiled as a pybind11 extension at install time. Performance-critical paths — IAU 2000A nutation evaluation, SPK/DAF kernel reading, apparent planetary evaluation (via NativePlanetaryEvaluator), coordinate transforms, light-time iteration, harmogram computation, precession, and event searching — execute natively without Python overhead.
This matters most in phenomenon-searching loops (retrograde periods, eclipse searches, heliacal events, conjunction sweeps) where core transforms are evaluated thousands of times. The native extension is a required component and is built automatically during pip install.
Moira is validated as a three-layer corpus. Each layer has its own correct evidence standard.
Astronomy layer — authoritative physical oracles first, enforced regression thereafter. References: IAU ERFA/SOFA, JPL Horizons, NASA catalogs, IERS.
Astrology layer — external chart software where stable and meaningful; doctrine-grounded invariants where no universal oracle exists. References: Swiss Ephemeris, Astro.com, canonical doctrine tables, structural invariants.
Experimental layer — subsystem-specific surfaces for sovereign or modern domains. Domains: sovereign fixed stars, variable stars, multiple star systems, galactic transforms, eclipse Saros classification.
Every validated claim must pass three gates:
When residuals remain, Moira documents them as model-basis differences rather than mislabeling them as engine defects. Two systems may be internally correct while answering different mathematical questions because of differing assumptions — for example, Delta-T branch, retarded-versus-geometric Moon treatment, or event-definition objective.
| Report | Verification Source |
|---|---|
VALIDATION_ASTRONOMY.md |
IAU ERFA/SOFA, JPL Horizons, NASA. Geocentric residual: 0.576 arcseconds (documented Delta-T divergence). |
VALIDATION_ASTROLOGY.md |
Swiss Ephemeris, Astro.com, canonical doctrine tables. Houses, ayanamshas, predictive cycles. |
VALIDATION_EXPERIMENTAL.md |
SOFA/ERFA, Swiss swetest, AAVSO, GCVS, binary orbit ephemerides. Sovereign stars, variable stars, multiple systems. |
graph TD
A[JPL Planetary Kernel\nChebyshev state vectors] --> B[SSB Barycentric Position\nkm · ICRF]
C[Sovereign Star Registry\n1809 named stars] --> D[Stellar Astrometric Position\nproper motion · parallax]
B --> E[1 · Light-Time Iteration\nbody at t − τ where τ = d/c]
E --> F[2 · Gravitational Deflection\nSun · Jupiter · Saturn · Earth]
F --> G[3 · Annual Aberration\nrelativistic · IAU SOFA]
G --> H[4 · IAU 2006 Frame Bias\nICRF → Mean Equator J2000]
D --> H
H --> I[5 · IAU 2006 Precession\nP03 polynomial series]
I --> J[6 · IAU 2000A Nutation\n1365 lunisolar + 687 planetary terms]
J --> K[True Equinox and Equator of Date]
K --> L[7 · Topocentric Parallax\nWGS-84 · optional]
K --> M[8 · Atmospheric Refraction\nSky positions only · optional]
K --> N[Ecliptic Projection\nTrue obliquity of date]
N --> O[Zodiacal Longitude · Latitude · Distance]
K --> P[Sidereal Frame · Ayanamsa\noptional]
K --> Q[House Cusps · 22 Systems\nrequires lat/lon]
The following traces every pipeline stage for Mars on 2000 January 1, 12:00 TT, using live DE441 kernel data. All numbers are from the running engine.
Time: JD_UT 2451545.000000 → JD_TT 2451545.000739 (ΔT = +63.807 s)
| Step | Operation | Vector / Value | Shift from Previous |
|---|---|---|---|
| 0 | DE441 kernel read — SSB → Mars | (206,980,508.6, −184,891.6, −5,666,529.8) km | — |
| 0 | DE441 kernel read — SSB → Earth | (−27,568,641.0, 132,361,060.2, 57,418,514.1) km | — |
| 0 | Geometric geocentric — Mars − Earth | distance: 276,697,408.2 km = 1.849608 AU | — |
| 1 | Light-time iteration — Mars at t − τ | τ = 0.010683 days = 15.383 min | 15.761 arcsec |
| 2 | Gravitational deflection — Sun + Jupiter + Saturn | sub-arcsecond bending of light path | 0.006 arcsec |
| 3 | Annual aberration — Earth velocity 29.786 km/s | relativistic displacement toward apex | 14.070 arcsec |
| 4 | IAU 2006 frame bias — ξ₀ = −16.617 mas, dε₀ = −6.819 mas | fixed ICRF → mean equinox J2000 rotation | 0.023 arcsec |
| 5 | IAU 2006 precession — P03 polynomial series | negligible at J2000 (reference epoch) | 0.016 arcsec |
| 6 | IAU 2000A nutation — Δψ = −13.932″, Δε = −5.769″ | true equator and equinox of date | 14.351 arcsec |
| 7 | Ecliptic projection — true obliquity ε = 23.437677° | λ = 327.963300° · β = −1.067779° · d = 1.849688 AU | — |
Final position: Aquarius 27° 57′ 48″ · distance 1.8497 AU · speed +0.7757°/day (direct)
Total pipeline correction from geometric to apparent: −43.760 arcsec
The largest contributors are nutation (−13.932″), annual aberration (−14.070″), and the combined light-time displacement (−15.761″). Gravitational deflection (0.006″) and frame bias (0.023″) are sub-arcsecond but non-negligible at sub-arcsecond accuracy targets.
Each correction stage can be toggled independently via planet_at(). The table below shows the measurable effect of disabling each stage on the Mars J2000.0 result.
| Parameter | Default | Effect on Mars J2000.0 longitude | Function |
|---|---|---|---|
apparent=True |
True |
Full pipeline active | planet_at() |
apparent=False |
— | Geometric position; all corrections skipped. Δ = +43.760 arcsec | planet_at() |
aberration=False |
— | Aberration stage skipped. Δ = +14.069 arcsec | planet_at() |
grav_deflection=False |
— | Deflection stage skipped. Δ = +0.003 arcsec | planet_at() |
nutation=False |
— | Nutation skipped; mean equinox used. Δ = +13.932 arcsec | planet_at() |
observer_lat/lon |
None |
When supplied, adds topocentric parallax (WGS-84). Effect: ~1° for Moon, <0.01″ beyond Jupiter | planet_at() |
refraction=True |
True |
Atmospheric refraction applied to altitude. Effect: ~0.57° at horizon | sky_position_at() |
delta_t_policy |
None |
Controls UT → TT conversion branch (IERS tables, polynomial, hybrid physical) | both |
The canonical documentation tree lives in wiki/. The flat moira.wiki/ Git wiki mirror is generated from it by python scripts/sync_git_wiki.py and should not be edited by hand.
| Document | Contents |
|---|---|
01_LIGHT_BOX_DOCTRINE.md |
Transparency and derivation as design constraints. |
BEYOND_SWISS_EPHEMERIS.md |
Capabilities enabled by sovereign catalogs, explicit policy, and modern Python. |
HOUSE_SYSTEM_DIVERGENCE.md |
House-system derivation and discretionary divergence from conventional Swiss-facing behavior. |
CONSTITUTIONAL_PROCESS.md |
The Subsystem Constitutional Process — the development and governance protocol. |
MOIRA_ROADMAP.md |
Feature implementation status and mathematical accuracy register. |
MIT (c) 2026 TheDaniel166. See PROVENANCE.md for license and Swiss-lineage provenance clarity.