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:
https://transit-list-demo.apps.poofc.com
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.
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
/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/. It uses /home/avalon/apps/svghanddraw/backend/assets/astro-font/astro-webfont.ttf by default; override with --astro-font /path/to/astro-webfont.ttf if needed.
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://transit-list-demo.apps.poofc.com/api/chart?date=YYYY-MM-DD&time=HH:mm&location=ENCODED_LOCATION'
Call:
curl -sS 'https://transit-list-demo.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.