Use this skill whenever the user asks for any of these:
Do not try to calculate timezone conversion or chart placements with local Python modules such as pytz, timezonefinder, flatlib, or Swiss Ephemeris inside the tenant. A Maya canary tenant once answered “I can't use the pytz module here” and then offered a generic summary; treat that as a regression. The correct behavior is to call the chart API first or clearly report that the chart service failed.
Astral Hermes tenants should use Alex's deployed chart service. It is now branded Astro Engine:
repo: firemountain/astro-engine
path: /home/avalon/apps/astro-engine
PM2: astro-engine
primary URL: https://astro-engine.apps.poofc.com
legacy alias: https://transit-list-demo.apps.poofc.com
Treat transit-list-demo in URLs/code as the legacy hostname/service lineage, not as the current product name. Prefer saying “Astro Engine” in user-facing summaries unless referring to an exact legacy URL, historical repo path, or dependent app still configured to that alias.
If a chart calculation tool/API call is possible, make the API call before interpreting. Do not tell the user “I can’t use pytz” and then offer a generic reading.
See references/maya-canary-chart-api-regression.md for the tenant regression that led to this rule and the astral-core@0.1.1 bundle fix.
Important response-shape note: GET /api/chart?... currently returns natal placements under data.natal.planets with readable sign labels in signName; older callers may expect data.chart.planets or data.planets. New integrations should accept all three shapes, with data.natal.planets first, and prefer signName over numeric sign when building user-facing labels.
GET /api/health
GET /api/time/resolve?date=YYYY-MM-DD&time=HH:mm[:ss]&location=LOCATION
GET /api/time/resolve?date=YYYY-MM-DD&time=HH:mm[:ss]&timezone=IANA_ZONE
GET /api/chart?date=YYYY-MM-DD&time=HH:mm[:ss]&location=LOCATION
GET /api/chart?utc=day/month/year/hour/minute/second&coordinates=LAT%20LON
GET /api/chart/svg?date=YYYY-MM-DD&time=HH:mm[:ss]&location=LOCATION
GET /api/chart/svg?utc=day/month/year/hour/minute/second&coordinates=LAT%20LON
GET /api/chart/svg?...&highlight=Body1,Body2
GET /api/chart/svg?...&transit=day/month/year/hour/minute/second&highlight=NatalBody,TransitBody
GET /api/transits/current
GET /api/search/stats
GET /api/charts
GET /api/charts/:id
POST /api/search/corpus
POST /api/search/similar
POST /api/search/time
/api/chart/svg returns SVG. Telegram usually needs PNG/JPEG for inline image delivery, so use the helper script in this skill to convert SVG to PNG and reply with MEDIA:/absolute/path.png.
After any successful chart compute, persist to the tenant KB so the next reading can recall_person and load_chart instead of recomputing or re-asking for birth data.
# Determine person slug (recall first; create if new)
KB=~/.hermes/skills/astrology/astral-tenant-kb/scripts/kb.py
python3 $KB recall_person "$NAME" >/tmp/p.json
# If null, then:
python3 $KB save_person "$SLUG" --name "$NAME" --birth '{"date":"...","time":"...","location":"...","utc":"...","coordinates":"...","tz":"..."}'
# Compute via /api/chart, then:
python3 $KB save_chart "$SLUG" --chart "@/tmp/chart.json" --birth "@/tmp/birth.json"
# Returns {"hash": "..."} — pass this hash into astrology-reading-compose so the saved reading
# carries chart_hash frontmatter (required for A/B comparison).
Rules:
- Idempotent by chart_hash — if load_chart returns a hit, skip recompute.
- Never write a chart without a person slug.
- This step is owned by the compose layer in normal reading flow, not by raw /api/chart calls. The chart-image helper script does not autosave (charts get cached only when tied to a known person).
Some older Magi/Astral code paths may still reference https://astrodataserver.com/chart or /draw. If those fail with TLS errors such as CERT_HAS_EXPIRED, do not bypass certificate validation. Move the integration to the self-hosted chart service instead:
natal=DD/MM/YYYY/HH/mm&location=LAT LON maps to GET /api/chart?utc=DD/MM/YYYY/HH/mm/0&coordinates=LAT%20LON./draw maps to GET /api/chart/svg?utc=DD/MM/YYYY/HH/mm/0&coordinates=LAT%20LON.When a chart image would help — natal chart, birth chart, aspect explanation, global transit wheel, biwheel/current transits — generate and send an image, not just text.
Telegram delivery rule:
transit-list-demo /api/chart/svg first and keep it as the source artifact.@resvg/resvg-js and supply the actual astro font file (/home/avalon/apps/svghanddraw/backend/assets/astro-font/astro-webfont.ttf). Preprocess font-family="A" to font-family="astro" for resvg/node font resolution; keep numeric labels on Arial/sans-serif. This preserves the transit-list-demo/Magi SVG style better than ImageMagick, which may ignore embedded WOFF fonts and show letters.9c40b9e and f04f329 stabilize Telegram chart rendering: planet/zodiac glyphs keep the astro font, house/degree/minute labels use regular DejaVu/Arial numerals, and SVG paths/background circles carry explicit fill="none" fallbacks so resvg does not paint a black chart background when CSS inheritance/nth-child selectors are incomplete.svghanddraw Rough.js renderer pipeline.Helper script:
node ~/.hermes/skills/astrology/astral-chart-api/scripts/chart-image-for-telegram.mjs \
--date 1985-09-26 \
--time 07:14 \
--location 'London, UK' \
--name Alex \
--json
Implementation note: this script now renders SVG→PNG with local @resvg/resvg-js from the skill directory, not ImageMagick. If it fails with Cannot find module '@resvg/resvg-js', run npm install in ~/.hermes/skills/astrology/astral-chart-api/. Font resolution order is: --astro-font override → the skill-local bundled font (assets/astro-webfont.ttf, which ships inside the astral-core bundle so it works in tenant containers) → the legacy host path /home/avalon/apps/svghanddraw/backend/assets/astro-font/astro-webfont.ttf → system fonts. It no longer hard-fails when the astro font is missing; it warns and rasterizes with system fonts so a usable PNG is still produced.
To rasterize an arbitrary local SVG (e.g. a Human Design bodygraph SVG from hd-prism-tenant-api) to PNG with the same renderer/font, use the companion script:
node ~/.hermes/skills/astrology/astral-chart-api/scripts/svg-to-png.mjs \
--in /tmp/bodygraph.svg --out /tmp/bodygraph.png --json
The script writes files under:
~/.hermes/astral-chart-images/
It prints JSON with:
{ "pngPath": "/absolute/path.png", "svgPath": "/absolute/path.svg", "media": "MEDIA:/absolute/path.png" }
Then send the image by placing the MEDIA: value in the final Telegram response.
svghanddrawAlex's chart styling repo is:
https://github.com/firemountain/svghanddraw
/home/avalon/apps/svghanddraw
Use this repo as the styling reference when chart images need to look good. See references/svghanddraw-styling.md for the customization JSON schema, the Telegram-clean recipe, and why the shipped default.json preset is wrong for delivery. Its method is not “make the stock SVG CSS louder”; it:
xmldom,For local POCs, use raw SVG from transit-list-demo, not astrodataserver.com. A working raw-source test script exists at:
/tmp/render-svghanddraw-from-transit.mjs
It writes:
~/.hermes/astral-chart-images/alex-raw-svg-transit-list-demo-through-svghanddraw.svg
~/.hermes/astral-chart-images/alex-raw-svg-transit-list-demo-through-svghanddraw.png
If future work needs production integration, port the svghanddraw/backend/chartService.js fetch layer from legacy /draw and /chart to:
GET https://transit-list-demo.apps.poofc.com/api/chart/svg?... # raw SVG
GET https://transit-list-demo.apps.poofc.com/api/chart?... # JSON
Do not add heavy styling rules into transit-list-demo itself. Keep transit-list-demo as the canonical raw SVG/data provider and apply visual rendering downstream.
Known svghanddraw/raw-SVG rendering pitfalls discovered with transit-list-demo:
F, D, k, etc. and font-family="A". Do not replace those with generic Unicode glyphs for final renders.astro-webfont.woff2/.woff) via @font-face just like Magi/transit-list-demo. In server-side node-canvas, use the converted actual font file backend/assets/astro-font/astro-webfont.ttf and render font-family="A" as the font's internal family astro.svg{font-size:7px} via embedded CSS, not font-size attributes on every <text>; a renderer that defaults text to 10px makes degree labels look oversized/misaligned.fillColor:none, white background, and padding.customizations.backgroundColor and customizations.canvasPaddingRatio if available to avoid Telegram/PNG transparent-black backgrounds and clipped outer labels.The chart renderer already supports Magi-style aspect highlighting through the third argument to engine.svg([chart1, chart2?, highlight]). The public SVG endpoint exposes that as highlight=Body1,Body2.
Use it when the answer is about a specific natal aspect:
node ~/.hermes/skills/astrology/astral-chart-api/scripts/chart-image-for-telegram.mjs \
--date 1985-09-26 \
--time 07:14 \
--location 'London, UK' \
--highlight 'Sun,Saturn' \
--label 'Alex Sun Saturn aspect' \
--json
For a single natal chart, highlight=Sun,Saturn highlights both bodies and the aspect line between them if the aspect exists under the service's orb rules.
For transits, the chart service draws a biwheel when transit= is supplied. Internally the SVG renderer treats chart1 as natal and chart2 as transit, so the highlight order for a biwheel must be:
highlight=NatalBody,TransitBody
This is intentionally different from the user-facing transit label, which should stay:
transiting body → aspect → natal body
Example: for user-facing Mercury Opposition natal Saturn, draw with highlight=Saturn,Mercury:
node ~/.hermes/skills/astrology/astral-chart-api/scripts/chart-image-for-telegram.mjs \
--utc '26/9/1985/6/14/0' \
--coordinates '51.5072178 -0.1275862' \
--transit '14/5/2026/21/14/28' \
--highlight 'Saturn,Mercury' \
--label 'Mercury Opposition natal Saturn' \
--json
For global/current transits, omit natal data and use --utc now or a specific UTC date as the single chart. Highlight in normal chart order:
node ~/.hermes/skills/astrology/astral-chart-api/scripts/chart-image-for-telegram.mjs \
--utc now \
--highlight 'Jupiter,Saturn' \
--label 'current Jupiter Saturn global aspect' \
--json
This skill is one piece of a 3-KB stack that backs every astrology reading. See references/astro-knowledge-topology.md for the full map (Astro Sources Wiki at /home/avalon/astro-sources-wiki with base id astro-sources, Human Design wiki, and the per-user/tenant KB prototype at ~/.hermes/astral-kb-prototype/knowledge/). The legacy /home/avalon/wiki path no longer exists — do not reference it.
curl -sS 'https://astro-engine.apps.poofc.com/api/chart?date=YYYY-MM-DD&time=HH:mm&location=ENCODED_LOCATION'
The legacy alias https://transit-list-demo.apps.poofc.com is retained for older dependent apps, but prefer the Astro Engine hostname in new examples and integrations.
Call:
curl -sS 'https://astro-engine.apps.poofc.com/api/transits/current'
If comparing current transits to a natal chart, calculate the natal chart first, then compare aspects/positions returned by the API.
For a user who asks “generate my chart” or “make Kathleen's chart,” do this:
If the API call fails:
Bad:
I can't use pytz here, but I can still interpret your chart...
Good:
I need the chart service to calculate the actual placements. The location/time lookup failed; can you give me the IANA timezone, e.g. America/Toronto, or should I retry with “Toronto, Ontario, Canada”?
User:
Generate a chart for Kathleen Brown, October 19 1994, 2:34 PM, Toronto Ontario.
Call:
curl -sS 'https://transit-list-demo.apps.poofc.com/api/chart?date=1994-10-19&time=14:34&location=Toronto%2C%20Ontario%2C%20Canada'
Then interpret from the API output, not from memory or guesswork.