Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
Tropical reveals the self you made. Sidereal reveals the self you were handed. That gap is AXIS.
LifestyleTypeScript · CSSGitHub @kayrmac23-droidOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Canonical for: what AXIS is, methodology, calculation accuracy, design tokens, setup and deployment basics. Lives elsewhere: architecture, core libs, the reading pipeline (models, quality gate, cost) and tests →
CLAUDE.md; product law →DOCTRINE.md. If this file andCLAUDE.mddisagree about code,CLAUDE.mdand the code win. Fix this file, don't follow it.
Tropical maps the psychological architecture of a self. Sidereal maps the incarnational conditions it navigates. The divergence is where this chart actually lives — AXIS holds both open.
Live: axis-astro.vercel.app
AXIS generates natal chart readings using both the Western Tropical and Vedic Sidereal zodiac systems simultaneously — and never merges them. The two systems disagree by the ayanamsa (~23.85°), and that disagreement is the product: AXIS computes both charts in full and holds open the distance between them. Divergence is not noise to be averaged out; it is the most informative part of the chart.
Most astrology apps pick one system. AXIS treats them as two maps of different layers of a person that do not resolve into one picture:
AXIS is written for readers who take astrology seriously — students of the technical literature, therapy-adjacent practitioners, those formed by the contemplative traditions. The interpretive vocabulary assumes a working understanding of both the Tropical and Sidereal frameworks before arriving. The chart is read once at depth rather than re-explained across a daily push notification.
Readings are written in continuous analytical prose. No bullet points. No generic affirmations. No predictions. Every sentence earns its place.
| Parameter | Choice | Rationale |
|---|---|---|
| House system | Whole Sign | Consistent for both Tropical and Sidereal; used throughout code, wheel, and interpretation engine |
| Ayanamsa | Lahiri | IAU standard for Jyotish; ~23.85° at J2000, precessing at 50.2564"/yr |
| Lunar nodes | True (osculating) node | Meeus Ch. 22 mean node + 4-term periodic corrections; matches Swiss Ephemeris to <0.05°. Oscillates ±1.5° around the mean with a ~173-day period. |
| Dasha system | Vimshottari | Used for current-chapter contextualisation, not event prediction |
| Midheaven | Shown as separate angle | MC is NOT the Whole Sign 10th-house cusp — displayed independently on the wheel |
Birth time sensitivity: Ascendant, house placements, MC, Moon degree, and dasha timing all depend on an accurate birth time. AXIS flags readings with an unknown birth time and discloses which elements are unreliable.
AXIS uses two complementary ephemeris sources:
1. Local VSOP87 / ELP2000 engine (astronomia package) — Sun, Moon, Mercury through Neptune, Rahu/Ketu. Full VSOP87 planetary theory and ELP2000 lunar theory. These are the same algorithms used in professional desktop astrology software.
2. JPL Horizons REST API — Pluto position. JPL Horizons (operated by NASA/JPL) returns positions computed from the JPL DE440 planetary ephemeris (Park et al. 2021), the current standard for solar system dynamics. DE440 accuracy for Pluto: < 1 arcsecond. AXIS fetches the Pluto position at chart calculation time via a single HTTP call to the Horizons API, with automatic fallback to the local Meeus polynomial if the API is unavailable.
| Body | Method | Accuracy (1800–2050) |
|---|---|---|
| Sun | VSOP87 via solar.apparentVSOP87 (nutation + aberration included) | < 1 arcsecond |
| Moon | ELP2000 + nutation correction | < 10 arcseconds |
| Mercury–Neptune | VSOP87 heliocentric + geocentric conversion + light-time + nutation | < 1 arcminute |
| Pluto | JPL Horizons DE440 (fallback: Meeus Ch. 37, ~15–60 arcmin) | < 1 arcsecond |
| Rahu/Ketu | True node: Meeus Ch. 22 + periodic corrections | < 0.05° vs. Swiss Ephemeris osculating node |
| Lahiri ayanamsa | Linear precession formula | Within ~0.01° of the IAU reference value |
Every generated chart displays the active Pluto ephemeris source ("JPL Horizons DE440" or "⚠ local fallback") in the reading header so users know which source was used.
AXIS requests apparent geocentric ecliptic longitude of date (Horizons OBSERVER quantity 31, CENTER=geocenter, APPARENT=AIRLESS). This is the same coordinate frame VSOP87 + nutation produces for all other planets — mean ecliptic and equinox of the tabulation date, light-time corrected, aberration included, no atmospheric refraction. The values are directly comparable and substitutable.
Horizons currently uses DE440 for dates in the 1550–2650 range. DE440 is the successor to DE431, published by Park et al. (2021). It is not Swiss Ephemeris (which wraps DE431/DE441 in a C library), but it uses the same underlying JPL integration and is at the same accuracy level.
swisseph (the npm wrapper for Swiss Ephemeris) requires a native C binary compiled for the target platform. Vercel Lambda runs in a read-only environment where pre-compiled C binaries cannot be shipped as part of a Next.js bundle. Additionally, JPL ephemeris data files are 30–180MB, exceeding Vercel's 50MB compressed bundle limit. These are hard architectural constraints, not tradeoffs.
The JPL Horizons API is the practical equivalent — same JPL data, same accuracy — accessed via HTTP instead of a native binary. For the 10 planets calculated via VSOP87/ELP2000, the current engine is already at Swiss Ephemeris accuracy. Horizons brings Pluto up to the same standard.
Ephemeris benchmark: node scripts/benchmark-pluto.mjs compares Meeus Ch.37 vs Horizons DE440 across representative dates (1930–2025) and reports the difference in arcminutes per date. See BENCHMARK.md for the full methodology and pre-computed Meeus values.
| Layer | Technology |
|---|---|
| Framework | Next.js 16.x (App Router) |
| Language | TypeScript |
| AI backend | Anthropic Claude Sonnet 5 for generation and Opus 5 for evaluation, via streaming API with prompt caching |
| Planetary calculations | VSOP87 (astronomia) + ELP2000 Moon + JPL Horizons DE440 Pluto |
| Geocoding | OpenStreetMap Nominatim |
| Timezone | tz-lookup (offline IANA lookup) |
| Deployment | Vercel |
| Fonts | Cormorant Garamond · Cinzel · Courier Prime |
AXIS uses a restrained dark-void palette. Copper is the primary accent, ratified July 2026 (the copper→gold migration is cancelled; there is no gold token). Cyan signals active computation, selected states, streaming, and focus. Violet marks unresolved tension, dissonance, and liminal states.
| Token | Value | Role |
|---|---|---|
--void | #02030A | True background black-blue |
--bg | #030212 | Page base |
--surface | #0C0B0E | Panels and form surfaces (neutral lifted base) |
--surface-2 | #131114 | Raised cards |
--reading-surface | #0C0B0E | Raised neutral surface behind long-form reading prose only (see DOCTRINE.md) |
--text | #EAE8F8 | Primary star-white text |
--text-2 | #A8A4C8 | Body support text |
--text-3 | #9490C4 | Labels and metadata |
--copper | #B87333 | Base identity accent — wordmark, primary CTAs, offset wedge, section rituals, symbolic emphasis |
--copper-bright | #D89455 | Fine linework, hairlines, chart ticks, small mono labels on void |
--copper-light | #F0B978 | Finest / dimmest-context lines; rare highlight or glow edge |
--copper-dim | #5A2F18 | Borders, shadows, subdued rules |
--copper-glow | rgba(184, 115, 51, 0.18) | Soft ambient accent |
--cyan | #2CC8C0 | Active computation and focus state |
--violet | #7844FF | Dissonance, contradiction, liminal insight |
Two-tier linework rule: base copper #B87333 is too dim for 1px lines and tiny type on the void. ALL fine linework, hairlines, chart ticks, and small mono labels use --copper-bright (or --copper-light for the finest/dimmest contexts). Only identity elements — wordmark, primary CTA fill, the offset wedge, section ritual rules — stay base --copper. No gradient blends two accent colours. All tokens are defined in src/app/globals.css.
Per-file architecture, every core lib and component, the reading pipeline and the test suite are documented in CLAUDE.md. They are not repeated here, because a second copy drifts. The top-level layout:
src/
├── app/ — pages: / (main app), /method, /guides, /sample, /synastry; globals.css holds the design tokens
│ └── api/ — calculate · synastry · geocode · timezone · reading (streaming Claude endpoint)
├── components/ — the dossier (DossierHeader, FrameControl, FrameShiftWheel, ReadoutRail, ReadingPanel), BirthForm, synastry panels, site chrome, landing/
└── lib/ — calculation engine, interpretation engine, prompts, quality gate, cache, reading thread
scripts/ — benchmark, eval harness, state checks
Both Tropical and Sidereal charts use Whole Sign houses throughout — for planet assignments, the chart wheel, and the interpretation engine. The MC (Midheaven) is computed separately as the ecliptic projection of the meridian and is displayed as an independent angle, not as the 10th-house cusp.
lib/interpretation-engine.ts sits between raw chart data and the Claude prompt. For each section it derives first-principles facts — essential dignity, sign modification, house environment, dispositor chain, aspects with applying/separating status, and a weighted evidence block for psychological axes (attachment/detachment, emotional suppression, etc.) — and formats them as a structured context block injected into every reading request.
The evidence-weighted approach (rather than categorical rules) means Claude receives the chart evidence, not pre-formed verdicts. For example, the Moon cross-reference for Sun and Mars sections lists specific attachment and detachment indicators with their weights, then states the evidence balance — so the interpretation can name the genuine tension rather than defaulting to a sign-element cliché.
Four reading types, each composed of sequential per-planet streaming requests to the Claude API. Per-section retry (2 attempts) prevents a single failed request from blocking the rest of the reading. Failed sections produce a visible placeholder so the readable portions remain intact.
Every generated section then goes through the LLM quality gate (lib/reading-quality-gate.ts), which is live. It runs as evaluate-and-recache after the section has finished streaming (gateForCache(), registered with Next's after()), so it adds no latency for the reader. It decides only what gets cached: the first reader of an uncached section sees the first pass, and every later reader gets the gated (and, where needed, repaired) text. The mechanics, thresholds and guards are in CLAUDE.md under /api/reading and lib/reading-quality-gate.ts.
synthesis, kept for cache-key stability) — Where the Chart Is Least Negotiable, Where They Part, The Central Tension, Living the Divergence. Its context block is a single deterministic SHARED DIVERGENCE PLAN (buildDivergencePlan) used unchanged by all four sections: every body in both frames with degree, dignity, retrogradation, sidereal nakshatra and sign-ruler condition; concordances in two grades (full, and same-sign with a house shift); divergences ranked by interpretive weight of the body and structural size of the shift; the aspects that link them; dasha timing and sidereal yogas; and explicit reliability limits when birth time is unknown. The reading names where the systems agree and where they pull apart, names the divergences it did not walk as still unresolved, and never averages the two charts into one answer.synastry-calc.ts computes inter-aspects (orb-limited, 5 major aspects) and a midpoint composite chart from both natal charts. For the composite-focused sections (composite_chart, central_dynamic), an elite chart block for the composite — dignity labels, chart ruler, and direction — is appended to the prompt context alongside the position table. The /api/synastry route handles calculation; SynastryReadingPanel streams the interpretation.| Variable | Description |
|---|---|
ANTHROPIC_API_KEY | Required. Set in Vercel dashboard → Environment Variables. |
UPSTASH_REDIS_REST_URL | Recommended. Upstash Redis REST endpoint for the reading cache. Create a free database at console.upstash.com. Falls back to no-op (no caching) when absent. |
UPSTASH_REDIS_REST_TOKEN | Recommended. Upstash Redis REST token (paired with the URL above). |
Operational switches for the reading pipeline (AXIS_READINGS_ENABLED, AXIS_GATE_ENABLED, AXIS_READING_RATE_LIMIT_MAX, …) are described in CLAUDE.md.
git clone https://github.com/kayrmac23-droid/Axis-astro.git
cd Axis-astro
npm install
Create a .env.local file:
ANTHROPIC_API_KEY=your_key_here
npm run dev # development server at http://localhost:5000
npm test # run test suite (vitest)
npm run lint # ESLint
npm run build # production build
App runs at http://localhost:5000.
Deployed via Vercel connected to this GitHub repo. Every push to main triggers an automatic production deployment.
Each API route sets its maxDuration via export const in the route file itself (the canonical Next.js App Router pattern; vercel.json only declares the framework). The route files hold the current values, and CLAUDE.md lists them.
This section describes what is hardened now and what requires distributed infrastructure for true production scale.
Hardened (current state):
next.config.js headers(): a scoped Content-Security-Policy (default-src 'self', frame-ancestors 'none', object-src 'none', base-uri 'self', allow-listed to the app's real external surface — PostHog and Vercel Insights), plus X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy (camera/microphone/geolocation denied), and Strict-Transport-Security (2-year HSTS with includeSubDomains; preload)/api/reading validates section and planetSection against explicit allow-lists before processing/api/reading recomputes chart positions server-side from validated birth data rather than trusting client-supplied positions, preventing cache poisoning via forged planet data/api/reading enforces a 16 KB payload size limit/api/reading applies a Redis-backed per-IP rate limiter (20 req / 60s fixed window via Upstash, with an atomic Lua INCR+EXPIRE; in-memory fallback when Redis env vars are absent)/api/calculate and /api/synastry enforce a 16 KB payload size limit with explicit JSON parse guard (reject oversized or malformed bodies before any processing)/api/calculate applies a Redis-backed per-IP rate limiter (30 req / 60s); /api/synastry applies 20 req / 60s/api/geocode enforces a 200-character query length limit and a Redis-backed per-IP rate limiter (30 req / 60s); results are field-whitelisted rather than forwarding raw Nominatim output/api/timezone applies a Redis-backed per-IP rate limiter (60 req / 60s) so the offline lookup cannot be used as an unbounded compute or scraping surfacestop_reason: max_tokens) are not cached and surface an inline notice to the user@upstash/redis). Persists across cold starts and is shared between concurrent serverless instances. 30-day TTL. Requires UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN env vars; falls back to no-op when absent.Distributed — no warm-instance bypass:
External service dependencies:
CLAUDE.md (/api/reading). Measured cost per reading comes from the [AXIS_USAGE] log lines or npm run eval. API key must be set in Vercel environment variables.ssd.jpl.nasa.gov) — one HTTP call per chart calculation for the Pluto position. Falls back to the local Meeus Ch. 37 polynomial (~15–60 arcmin vs DE440 across 1930–2025; see BENCHMARK.md) if unreachable.astronomia/pluto; ~15–60 arcminutes vs DE440) and the chart header displays "⚠ local fallback." The fallback is noted so users know the accuracy tier their chart used. See BENCHMARK.md.ssd.jpl.nasa.gov per chart calculation for the Pluto position. If the endpoint is unreachable (downtime, network policy), the fallback engine activates transparently.