How Astralium handles your data: defences and boundaries

Layer by layer: charts computed in your browser, history kept local, card details never reaching our servers, cross-account isolation enforced by version-controlled row-level security, and the seven-day observation behind the CSP.

This page documents how Astralium handles user data: where the data travels, who holds each segment, and how the controls are verified. The settings and figures below can be checked against this site's public HTTP response headers and the database migrations in the repository.

The final section states what this page does not cover.

1 · Where the data goes

Charts are computed in the browser

All six engines — Zi Wei Dou Shu, BaZi, Western astrology, Vedic astrology, Liu Yao and Qi Men — are bundled with the front end. Star placements, transformations, stem-branch relations, houses and aspects are computed in the user's browser and do not pass through our servers.

Chart history is local

History is stored in the browser's localStorage as a fixed-size ring buffer, partitioned by account. While signed out, it exists only on that device.

Geocoding goes through the server

A place name must be converted to coordinates and a timezone before true solar time can be computed. This runs through the site's own endpoint, /api/resolve-birth-place: the server holds the third-party maps key and caches results, so the key is never shipped to the browser and a repeated place name is not looked up twice.

When data is written to the database

Used signed out, none of the above produces a database write. Data reaches Supabase only when a user saves a birth profile, or a practitioner creates a client, a case, or a packet version.

Two categories of data leave the browser: the birth place name required for geocoding, and records the user explicitly saves.
2 · Browser-layer controls

Response headers currently in effect

Five security headers are served site-wide. The CSP reads in full:

frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; script-src 'self' https://challenges.cloudflare.com https://connect.facebook.net
  • Content-Security-Policy — restricts which sources may execute script; see below
  • Strict-Transport-Security — enforces HTTPS, no downgrade
  • X-Frame-Options: DENY with frame-ancestors 'none' — no origin may embed the site in an iframe (clickjacking)
  • Referrer-Policy: strict-origin-when-cross-origin — sends only the origin across sites, never the full URL
  • Permissions-Policy — disables camera, microphone, geolocation, the payment interface and USB

The set is rated A+ by securityheaders.com.

securityheaders.com report for getastralium.com showing an A+ grade with all six security headers present
securityheaders.com report for getastralium.com, 1 September 2026. The same scan can be re-run at any time.
Among metaphysics sites in this category, an A+ is currently close to unheard of. You do not have to take our word for that — enter the domain of any astrology, divination or charting site at securityheaders.com to see its grade, along with a per-header breakdown of what is present and what is missing.

script-src without unsafe-inline

This site's script-src carries neither 'unsafe-inline' nor 'unsafe-eval'. That is the strongest form of protection a CSP can provide. In effect, none of the following execute:

  • injected inline scripts
  • event-handler attributes such as onerror
  • javascript: URLs
  • eval and string-form setTimeout
  • external scripts from unlisted origins

This build contains zero inline executable scripts, checked across all 329 prerendered HTML files; its 397 JavaScript chunks contain no eval, no new Function, and no WebAssembly compilation call.

By contrast, 'unsafe-inline' permits the page to execute any inline script; a script-src carrying it offers close to no protection against cross-site scripting, because an injected inline script still runs. It is common in deployed policies not because the configuration is difficult, but because removing it is not a setting — it is a constraint on how the front end is written: the HTML served to the browser must contain no inline executable script. For a site already using inline script, removing it generally costs a front-end rewrite.

The same tool prints each site's full script-src, so whether it carries 'unsafe-inline' is visible at a glance. A site with no content-security-policy header at all has no CSP.

connect.facebook.net in the allowlist

The script-src allowlist includes connect.facebook.net, but this site does not load that script. Meta's in-app browsers (Threads, Instagram, Facebook) inject it into every page they render, for advertising conversion attribution; it is listed so that attribution continues to work for traffic arriving from those sources.

The cost is stated alongside it: an origin in the allowlist can execute script on this site's origin, which ties this site's security posture to that origin's. It is the same trade every site running Meta conversion tracking makes. The entry is unrelated to functionality — removing it affects no operation, and only disables advertising attribution through that channel.

default-src is omitted

The policy sets no default-src. Directives that are not listed therefore remain unrestricted, so adding a font, an image source or an embedded page cannot break the site through CSP.

This trades coverage for maintenance surface. With default-src omitted, the situations requiring a policy change reduce to one: adding an external script source to the front end. The reason is survival of the policy itself — a policy that regularly breaks routine development gets loosened during some debugging session and is not restored afterwards.

3 · Third-party boundaries

Who holds each segment

  • Payments · Stripe — checkout is a full-page redirect to a Stripe-hosted page. The front end does not install @stripe/stripe-js. Card details pass through neither our pages nor our servers; the server receives subscription state returned by Stripe.
  • Database and authentication · Supabase — the boundary between accounts is drawn by Row Level Security policies, detailed below.
  • Transport and deployment · Vercel — TLS certificates, edge network and the deployment pipeline.
  • Error monitoring · Sentry — configured with sendDefaultPii: false, session replay and tracing disabled. It collects error type, stack and browser environment; no default personal identifiers, no session recording.
4 · How the controls are verified

Cross-account isolation is enforced by the database

Row-level security is enabled on 18 tables, in two shapes:

  • 13 carry user-scoped policies — 40 in total, each constraining auth.uid() to match the row's owner.
  • 5 carry no policies at all — the geocoding cache, feedback submissions, the referral abuse log and two Stripe idempotency tables. Row-level security denies by default, so these are unreadable with any user credential; only the server-side key reaches them.

The read policy on birth profiles, for instance:

create policy "profiles_select_own" on public.profiles for select using (auth.uid() = user_id);

The rule sits in the database rather than the application. Querying directly with a user's credentials, bypassing the application, still returns no rows belonging to anyone else.

These policies are written in version-controlled migration files rather than configured through a dashboard. Every change carries a commit record and passes review.

Isolation is tested automatically

Correct configuration and correct behaviour have to be confirmed separately. A verifier in the repository creates two temporary accounts, attempts to read one account's rows using the other's identity, asserts the result is empty, and deletes both accounts.

The observation period before enforcement

A policy that blocks requests fails, when misconfigured, by silently breaking page functionality. The policy was therefore deployed first as Content-Security-Policy-Report-Only — recording without blocking — and observed against real traffic for seven days.

None of the violations recorded during that window originated in this site's own code; all came from tracking scripts injected by applications' built-in browsers. Only then was the policy switched to enforcing. On the day of that change, controlled test scripts were injected on production to confirm blocking was actually in effect.

5 · Scope and limits

How far this page goes

Risk classes covered here

    • cross-site script execution
    • clickjacking
    • connection downgrade
    • cross-account reads
    • handling of payment details

Not covered here

    • providers' own practices and compliance (Supabase / Vercel / Stripe each publish their own)
    • the strength of a user's password
    • whether a device is shared

These measures address specific classes of risk. They are not equivalent to "secure" as an overall state.

Practitioners who need to explain data handling to clients may cite this page directly. For anything not covered, please write viaContact.

Casting requires no account.

Signed out, no database write occurs. Sign in when you want to save profiles.

Start charting →