Build Dreams & Stars

KINDAback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A personal dream and chart journal whose hard problems are getting the astronomy from an ephemeris rather than from a model, handling birth data whose time is unknown and whose time zone is historical, and keeping a wellness-shaped app honest about what it is and what it will not answer.

estimated effort weekendthe files for this build are in the project pack

RuntimeNext.js 15 with TypeScript, single user, running locallyAstronomyAn ephemeris library, with its licence recorded in the READMETimeA time zone database with historical rules, plus an explicit unknown-time pathGeocodingA public geocoder used inside its published usage policy, every result cachedStorageSQLite on the local disk, no account, no upload, no telemetry

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the application.

    Get it nodejs.org, or your version manager. open ↗

    Verify node --version prints v22 or higher

  2. installfree under its open-source licence; a paid licence exists for closed distribution

    Why Every planetary position in this build comes from here, and the library's licence constrains what you may do with the result.

    Get it Install the bindings for your runtime, fetch the data files, and read the licence terms in full before you build on it.

    Verify Five known dates match a published source within your stated tolerance

  3. API keypay per call; a reading is fractions of a penny

    Why The interpretation is one call per reading and is the only generated part of the product.

    Get it Create a key with the provider you prefer and set a spend limit in their console as well as the ceiling here.

    Verify One reading generates in the configured language with its model identifier stored

  4. free within the published policy

    Why Birthplace lookup is the one thing here that hits somebody else's free service, and their policy sets the rate, the identification requirement and the caching obligation.

    Get it Read the policy for the geocoder you choose, note the maximum rate and the identification requirement, and record the date you read it. open ↗

    Verify Your client sends an identifying user agent, respects the stated rate, and caches every result

  5. decidefree

    Why The safety check refuses to interpret certain entries and has to offer something real in their place, which is local and cannot be guessed by the build.

    Get it Look up the current helpline numbers and text services where you live and write them into setup.

    Verify Setup refuses to complete with the field empty and the contacts render on a flagged entry

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- people(id, label, birth_date, birth_time, time_known, birth_place_id, created_at) · time_known is a real column because an unknown birth time changes what can be computed
- places(id, query, display_name, lat, lon, tz_id, source, fetched_at) · the geocoder is called once per place and never again
- charts(id, person_id, ephemeris_name, ephemeris_version, computed_at, positions_json, aspects_json, houses_json, houses_omitted_reason)
- entries(id, kind, body, created_at, language, mood) · kind is dream or question
- readings(id, entry_id, chart_id, body_md, model_id, prompt_version, language, layers_json, created_at, cost_minor)
- flags(entry_id, reason, at) · set by the pre-call safety check, and a flagged entry has no reading row
- settings(crisis_contacts_md, monthly_cost_ceiling_minor, default_language)
- A chart with no birth time has no houses and no rising sign, and the row records why rather than filling them in from a guessed noon.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_PATHrequired./data/journal.dbPeople, charts, entries, readings and flags. The diary.
EPHEMERIS_PATHrequired./data/epheEphemeris data files. Its version is stored on every chart.
MODEL_API_KEYsecretrequiredsk-...redactedThe text model. Set a provider-side spend limit as well.
GEOCODER_USER_AGENTrequireddream-journal/1.0 (you@example.com)Identifies your client as the geocoder's policy requires. A default library agent is not acceptable.
GEOCODER_MIN_INTERVAL_MSrequired1000Minimum gap between geocoder calls. Check the current published policy before lowering it.
DEFAULT_LANGUAGErequiredenThe whole reading, headings included, is produced in one language.
MONTHLY_COST_CEILINGrequired3.00Readings stop here. Journal, charts and history keep working.

The build, in order

  1. Astronomy from an ephemeris, checked against published positions

    Planetary positions and aspects are computed by a library and verified against known values.

    1. The licence affects what you may do with the finished thing.

      Files README.mdsrc/astro/ephemeris.ts

    2. Five dates in the suite, with a stated tolerance.

      Files tests/positions.test.tsfixtures/positions.json

    3. The prose is a view of the data, and it is regenerable.

      Files src/astro/chart.ts

    done when · tick each as it passes
    watch out
    • Asking a language model where Saturn was on a given date produces a plausible degree and sign that is wrong often enough to be useless and confident enough that nobody checks, and every later layer of the product inherits the error. Compute from the ephemeris, store the numbers, and have the model read the numbers rather than supply them.
  2. Birth data is the hard input

    Unknown times are handled honestly and places are geocoded once, inside the geocoder's published policy.

    1. It omits houses and the rising sign and explains itself.

      Files src/people/birth.tstests/unknown-time.test.ts

    2. A 1978 birth is not converted with a 2026 offset.

      Files src/astro/time.tstests/tz-history.test.ts

    3. Read the policy, set an identifying user agent, rate limit to it.

      Files src/geo/lookup.tstests/geo-cache.test.ts

    done when · tick each as it passes
    watch out
    • Defaulting an unknown birth time to noon silently produces a rising sign and a full set of houses that are simply invented, and they are the first things the reading talks about. Record time_known, omit everything that depends on it, and say in the output which parts could not be computed and why. The same honesty applies to the offset: a birth in 1978 uses that year's rules for that zone, which a naive conversion from the current offset will get wrong by an hour or more.
  3. The safety check runs before the model, not after

    An entry describing crisis is saved, flagged and answered with your configured local contacts instead of an interpretation.

    1. A spy in the suite proves no call happened.

      Files src/safety/screen.tstests/safety.test.ts

    2. Local to the person, because a generic number helps nobody.

      Files src/setup/contacts.ts

    3. The journal keeps the words; the app declines the reading.

      Files src/entries/create.ts

    done when · tick each as it passes
    watch out
    • Running the check on the model's output means the generation has already happened and the person has already been shown an interpretation of the worst night of their life by a dream app. Check the input before any call is made, and treat a false positive as an acceptable cost, because the failure in the other direction is not.
  4. Readings that stay inside what they are

    Generated text is layered, labelled, single-language, and checked against the things it must not say.

    1. The chart data goes in; interpretation comes out.

      Files src/readings/generate.tssrc/readings/prompts.ts

    2. Headings included, and check rather than trust.

      Files src/readings/language.tstests/language.test.ts

    3. One regeneration, then a failure the person can see.

      Files src/readings/guard.tstests/guard.test.ts

    done when · tick each as it passes
    watch out
    • Translating an English reading into another language after generating it produces headings in one language and body text in another and idioms that land strangely, and people notice immediately. Pass the language into the prompt, require the entire response in it, and check the output before storing rather than trusting the instruction.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more wellness.