Ask Horary
FOR DEVELOPERS · VERSION 0.2

Inside Horary.

Astronomical calculation, application rules and generated text are separate parts of the system.

Swiss Ephemeris and the Python wrapper.

Swiss Ephemeris by Astrodienst calculates planetary positions and speeds, the ascendant and Regiomontanus house cusps. We call swe.calc_ut with FLG_MOSEPH | FLG_SPEED and swe.houses(..., b'R'). Planetary calculations use the built-in Moshier algorithm, without external ephemeris files.

The installed wrapper is pysweph 2.10.3.6, imported as swisseph. It continues pyswisseph, but the packages are not completely interchangeable: the application handles pysweph's unused house-array element zero.

Our code calculates seven traditional planets, signs, houses, retrograde motion and the rulers of houses one and seven or ten. Major aspects use 0°, 60°, 90°, 120° and 180°, with a fixed 6° display orb. SVG charts use the same calculated data; PNG export runs in the browser.

What about Kerykeion?

Kerykeion is a higher-level astrology and charting library. It is not installed here: our code performs this layer's work on top of Swiss Ephemeris. Adding a library alone would not provide complete horary interpretation.

From the browser to the archive.

A random HttpOnly cookie identifies the browser owner; the database stores its SHA-256 hash. Production cookies use Secure and SameSite=Strict. Public sharing tokens are passed in the URL fragment and then sent to the API in a POST body. Private read, share and delete operations check ownership.

Current model: GPT-4.1 mini.

GPT-4.1 minigpt-4.1-mini-2025-04-14Model documentation ↗OpenAI model catalog ↗

The Chat Completions request contains the question, topic, chart and limited rules. There is no web search or previous-reading context. Output is JSON, with a 1,400-token cap, temperature 0.3 and a 45-second timeout. One attempt makes one request, without automatic retries.

Validation checks completion, status, text fields, one to three factors, existing fact IDs and allowed rule IDs. This is structural validation, not proof that the interpretation logically follows. Stored answers include model, prompt version, language and token usage.

The owner selects the model using HORARY_AI_MODEL and redeploys. An unapproved value disables new readings. Historical answers are not regenerated when the model or interface language changes.

Budget reservations.

A PostgreSQL transaction with an advisory lock enforces 10 reading attempts per browser over 24 hours, one attempt per IP per minute, and 380 paid calls per UTC calendar month. Readings and transcription share that monthly limit. Each permitted call reserves $0.05, without refunds on provider errors. The $19 reservation ceiling leaves $1 of a $20 budget as a buffer.

Input byte caps are 30,000 for mini and 12,000 for GPT-4.1. Pricing and limits must be reviewed when adding models. The application reservation is not the OpenAI account balance or a billing control on that account.

A calculated chart is not a complete horary judgement.

Dignities, receptions, translation and collection of light, void-of-course Moon and full aspect perfection before a sign change or station are not assessed. An applying flag only means the deviation from exactitude decreases one hour later.

The fixed 6° orb is a simplification. Absolute latitudes of 66° or more are rejected. Warnings about early or late ascendants, Saturn in the seventh house or shared rulers do not produce automatic verdicts. Expert review and reference cases are still needed.

Voice input.

MediaRecorder captures up to 30 seconds. Web Audio displays microphone levels and converts audio to mono PCM WAV at 16 kHz. The server validates the format and actual duration before calling OpenAI GPT-4o mini Transcribe. The selected language is passed to transcription. Audio is not stored. Users review the draft before submitting.

Voice attempts have separate daily and minute limits but share the monthly reservation budget. Cancelling an already sent request does not refund its reservation.

Past and future events.

Swiss Ephemeris finds each planet's nearest sign ingress, nearest retrograde and direct station, and nearest major aspect for each pair in either direction. The Sun and Moon have no stations. Search extends 1,096 days each way, using geocentric tropical longitudes, Moshier, six-hour brackets and root refinement below one second. Numerical precision is not a claim of subsecond physical accuracy.

Times are UTC. Positions refer to the first planet in each row; ingress rows show the new sign. This selected list is not a complete calendar and may differ from Sotis. Events are computed for display, including old readings; they are not passed to the interpretation model.

Languages and owner statistics.

Locales are registered centrally in i18n.py. Interface catalogs, server labels and editorial content are separate from calculations. English and Russian routes use language-specific canonical and alternate links. Calculations remain identical across languages.

The owner dashboard at /admin uses a separate password, signed eight-hour HttpOnly sessions and persistent login throttling. It returns aggregates only: saved readings, browser archives, languages, topics, models, feedback, recorded tokens and monthly reservations. Deleted readings and failed attempts are absent from report counts. Reservation totals include all admitted paid calls and are not actual spend.

Inspect the implementation.

Source code · AGPL-3.0-or-later ↗

Swiss Ephemeris programming documentation ↗

Back to the plain-language guide ↗