Skip to main content

Shamash

This aims to be a physically accurate worldbuilder. A lot of these calculations are rather educated guesses than well-founded scientific principals, but they all have their foundation in recent scientific discoveries and laws of nature.

Based on the great Artifexian's Work "The Worldsmith" I wanted to continue his journey. Intruiged by building on the very idea of quantifying reality - but at the same time horrified by looking at a spreadsheet - the only logical next step was to build a python application. :)

So, before the whole thing became a webfrontend with an API I designed it to be a CLI-Application. I like to work from the terminal, but after having hundreds of parameters scrolling past your tired eyes this was just not feasable any more. So I turned the CLI into an API (which still can be used as CLI as well) and created a React-Frontend.

Worldbuilding is an incredibly complex process. This application will never aim at calculating EVERYTHING, you know. I know some of you (including me) have those thoughts and it is only natural to desire a feeling of absolute control over reality. Understanding, caluclating, quantifiying everything until its smallest detail. But look into yourself. The motivation will most likely be fear, or some derivation of it. You are afraid to not be able to hold the world you are building in the palm of your hand.

But the very thing you are seeking to do - describing a reality by natural laws - is something we humans perfected since 300.000 years by immitating our natural surroundings (impressions), using them to express our feelings, pain and pleasure (expressions). This enables you to describe your newly formed reality in a way far more compley than any table of figures can. Put your world into writing, not into a table, so other humans can comprehend what you are creating.

The shamash repositories

shamash is split by audience:

  • shamash-core (this repo) — the physics engine and the shamash CLI. Published on PyPI.
  • shamash-server — the HTTP API around the engine, dockerized.
  • shamash-web — the React frontend, dockerized, talking to a shamash-server.

Getting started

pip install shamash-core
shamash -p

The distribution is shamash-core; the command and the import name are both shamash.

For development, a Makefile is provided:

make venv
uv pip install --python venv/bin/python -e ../naturedynamics   # the physics backend, as a sibling checkout
make install
make run
make test

Without any paramaters, shamash reads the seed world packaged at src/shamash/data/default.yml, which represents our very solar system. make run is configured to use the -p flag, which pretty-prints out all calculations.

The CLI application

shamash is the terminal-first way to use this project — load a world, optionally change what gets read or written, and exit. A plain shamash prints nothing; it just loads the world and returns, which is what makes it safe to script. Every flag is optional and they compose:

shamash                    # load src/shamash/data/default.yml, print nothing
shamash -w my_world.yml    # load a different world file instead
shamash -p                 # also pretty-print the loaded (and computed) world to stdout
shamash -e out.yml         # also export the loaded world to out.yml
shamash -f json            # write JSON instead of YAML (also: csv)
shamash -i new_world.yml   # copy the input world to new_world.yml, to seed a new one, then exit
shamash -l                 # also log to the configured log file (stderr always gets the log otherwise)

-p and -e render through the exact same writer, so shamash -p > world.yml produces a file that reloads with -w byte-for-byte. That holds per format: -f json -e world.json reloads with -w too, and gives back the identical world. On a terminal, -p syntax-highlights the output with rich; piped or redirected, it falls back to the plain string, so the redirect stays clean.

-f csv is the flat one, and the exception: one row per scalar rather than a tree, entity,type,property,source,value,unit,dimension, with compound values exploded into rows of their own (main_star_apparent_size[0], atm_composition.o_2). It is the same view shamash-web's matrix tab shows, and it is one-way — a world cannot be read back from it. Use it to pull a computed world into a spreadsheet or a plotting script:

shamash -f csv -p | awk -F, '$3 == "habitability"'

Verbs

Flags act on a world; verbs change one. create makes one entity and hangs it off an existing one:

shamash create Atmosphere earth -w my.yml        # asks for what it needs
shamash create Planet sol -w my.yml              # offers sol's free orbits, or makes one
shamash create Star --id alpha --set mass=1.0:MSol --set current_age=4.6:Gyr -w my.yml

Flags work on either side of the verb, so shamash -w my.yml create Orbit sol reads the same.

Required fields it isn't given, it asks for; --set key=value answers one up front. --set also reaches the optional fields it never asks about — a planet's core (--set core_density=10900), a star's wind — and refuses a name the type doesn't have, so a typo is a visible error rather than a value that quietly never arrives. Values are SI unless you give a unit — --set mass=1.0:MSol goes through the same conversion and the same checks a world file's {value: 1.0, unit: MSol} does, so a wrong dimension is refused rather than quietly scaled.

Choices with no safe default are asked too: a body that already has an atmosphere, a parent that already has orbits, a file about to be overwritten. --answer name=value settles those up front (--answer strategy=add), which is what makes the whole thing scriptable — including questions about an entity create makes along the way, like the orbit a new planet needs (--answer semi_major_axis=1.52:ASol). With nothing to ask and no terminal to ask at, it refuses instead of hanging — a piped run tells you exactly which answers it was missing and exits 1.

What can host what is not a list anywhere; each relationship declares what the child reads off its parent, and a type qualifies by providing it:

$ shamash create Atmosphere sol -w my.yml
Atmosphere cannot hang off 'sol': a Star has no surface_temperature,
which Atmosphere.parent_moon_or_planet needs. Types that do: Planet.

That sentence is assembled from the models every time, so it follows them — the day a Moon can compute a surface_temperature, moon atmospheres start working and this refusal stops being produced, with nothing to update.

create writes back to the file -w names, or to -e when given. It refuses without one of them (the default world lives inside the installed package), asks before overwriting unless -y, and refuses -f csv, which does not reload.

crs is the read-only one: it prints a coordinate reference system for a body's surface, so a world can be mapped in QGIS, GDAL or anything else built on PROJ:

shamash crs earth > earth.wkt                               # latitude/longitude
shamash crs earth --projection north-polar                  # or equirectangular, south-polar,
shamash crs earth_moon_1 --projection mollweide             #    sinusoidal, transverse-mercator
shamash crs earth --projection equirectangular --central-meridian 180

It writes WKT2 for a sphere of the body's radius, shaped like the IAU's own planetary CRSs (which PROJ ships as IAU_2015:*): planetocentric latitude, east-positive longitude, a reference meridian at 0. Anything with a radius qualifies — a Star, Planet or Moon; an Atmosphere or Orbit is refused, naming the types that do.

One catch that is PROJ's, not ours: PROJ decides which celestial body a CRS belongs to from its radius alone, and treats any sphere of roughly 6,330–6,423 km as Earth. A fictional world that close to Earth's size will transform to WGS 84 without complaint; anything further off is refused, as it should be.

As a library

Everything the CLI does is reachable from Python. A world loads into a registry, every value on it is computed on access, and Worldloader turns it back into text:

import shamash as sh

with sh.god.scope():
    loader = sh.services.Worldloader()
    loader.load(sh.services.BaseService.default_world_path())
    print(sh.god.summons("earth").habitability)
    print(loader.dumps(fmt="json"))

god.scope() gives the world a registry of its own, so two worlds can be computed at once without seeing each other — that is what shamash-server does per request.

sh.utils.describe_entities() is the per-type schema — every field and computed property with its unit, whether it is required, and its description — derived from the models rather than written down anywhere.

Property documentation

Every field and computed property carries a description, and each has a long-form page with formulas and sources under src/shamash/docs/properties/<Type>/<name>.md. The pages ship inside the package, so a server always hands out the pages for exactly the engine version it runs; shamash-web renders them. make test fails if a description has no page or a page documents nothing.

Releases

Development happens on origin (dev.fs.or.at). A release is an annotated tag on main, and codeberg only ever sees tagged states:

git push origin main                          # every day
git tag -a v2026.1.1 -m "CLI verbs and output formats"
git push origin --tags                        # keep the markers on dev too
git push codeberg main v2026.1.1              # publish

Tags go to both remotes; commits go to origin until they are released. Then the same commit goes to PyPI:

make publish-test   # TestPyPI, with $PYPI_TEST_TOKEN; check it installs
make publish        # PyPI, with $PYPI_TOKEN

That way the release history survives on the remote that has everything, rather than only on the one it is published to.

So git tag is the list of releases, each heading below names one, and the version in pyproject.toml is bumped in the commit the tag points at. __version__.py reads it from there — see the note in CLAUDE.md about why the checkout's pyproject.toml wins over importlib.metadata.

2026.3.1

  • crs argument mistakes are typed: an unknown projection, or a polar projection given a central meridian, raises InvalidProjectionError instead of a bare ValueError, so shamash-server can answer them with a 422 rather than a 500. The CLI behaves as before.

2026.3.0

  • A terrestrial service and the crs verb
    • services/terrestrial/ is the home for anything on a body's surface. First in: WKT2 coordinate reference systems for any entity with a radius — geographic, equirectangular, polar stereographic, sinusoidal, Mollweide and transverse Mercator — mirroring the IAU 2015 definitions PROJ ships.
    • A sphere for now: a rotational ellipsoid needs a flattening model, and the simple homogeneous one gets Earth wrong (1/232 against 1/298).
    • Tested against PROJ itself through pyproj (a dev-only dependency): every CRS parses, and known points land where the closed-form projection says they must.

2026.2.0

  • Split the project into repositories
    • This repo becomes shamash-core: the engine and the CLI, published on PyPI as shamash-core. The import name and the command stay shamash.
    • The HTTP layer moved to shamash-server and the frontend to shamash-web, both dockerized; the marimo notebooks to a private experiments repo. shamash-api is gone from this package.
    • The property docs moved into the package (src/shamash/docs/properties/), next to the descriptions they expand on, so a page and its description change in one commit. The coverage test moved with them and now checks both directions, the Settings pages and the sidebar — it used to check only that entity descriptions had a page, and skipped itself in any install without the frontend.
    • describe_entities/describe_config moved from api/schemas.py to sh.utils: they read only the models.
    • MIT licensed, in all three public repos.
    • Dependencies cut to what the engine imports: pyyaml, rich, naturedynamics. numpy/scipy/matplotlib arrive through naturedynamics, which declares them itself.

2026.1.2

  • Bring back a test suite — make test, stdlib unittest, ~1 s
    • It loads the real seed world and asserts what comes out, rather than mocking collaborators: the physics is a pure function of default.yml, so running it is the test. Most of it is a transcription of numbers CLAUDE.md already presented as regression baselines — Sol at 1.000 RSol / 5770.8 K / G2.9, Earth habitability 1.000, #B3F49A, magnetopause 9.52 R⊕ — which described a suite nobody had written down.
    • 70 tests over the physics anchors, the loader's round-trips and error taxonomy, god.scope() isolation under 8 threads, the HTTP contracts (since moved to shamash-server's suite), the CLI, and the create verb's parent rules.
    • Mutation-probed while being written, which is the only reason to trust it: feeding Star.radius kilograms fails 6 tests, dropping FACTOR_FLOOR fails the hostile-world test, removing the Planet.moons backref fails the tidal one.
    • make test-packaging is separate (~15 s): it builds a wheel and runs the CLI and the API from it with no source tree in sight.
  • Two bugs the suite found before it was finished
    • create Moon sol ran the orbit cascade before discovering a Star cannot host a moon, so it created an orbit and then refused — leaving it behind in a world the caller was told nothing had been added to. Legality is now decided first, so a refusal costs nothing.
    • create asked its questions before validating --set names and the --id, so a typo was reported only after you had answered three prompts. Both are checked up front now.
  • Corrected numbers the docs stated but the code did not produce
    • magnetosphere.py documented Earth's magnetopause at 9.6 planetary radii and the bow shock at 12.5, in five places including user-facing text mirrored into the docs site. The code computes 9.52 and 12.37.

2026.1.1

  • Output formats for the CLI — -f yaml|json|csv
    • Notation moved out of both the codec and the HTTP layer into services/world/formats.py, so there is one YAML dumper call and one JSON dumper call in the codebase and the CLI and API cannot drift. -p and -e are byte-identical per format; shamash -f json -p is byte-identical to GET /v1/world/default.
    • csv is the flat view — one row per scalar, the same shape shamash-web's matrix tab shows — and is one-way.
    • Worldloader.loads now parses a root-level { as JSON. PyYAML follows YAML 1.1, where 5e-05 without a dot is a string, so the app could not read back a file it had just written with -f json -e.
  • CLI verbs — shamash create <Type> <parent_id>
    • cli.py grows subparsers and a VERBS map; flags work on either side of the verb. -w names the file the verb modifies.
    • What can host what is declared per relationship as parent_needs — what the child reads off its parent — never as a table of types. The refusal message is assembled from the models, so it follows them: the day a Moon can compute a surface_temperature, moon atmospheres start working with nothing to update.
    • Services never prompt. They call self.ask(questions); the CLI injects InteractionService.ask and everything else gets refuse_to_ask, which raises a 422 carrying the questions — a service reading stdin would block a uvicorn worker forever, and world:create-orbit runs the same code.
    • --set and --answer make a create fully scriptable; with nothing left to ask it needs no terminal at all.

2026.1.0

  • Docker, and a React web frontend — both now live in their own repos, shamash-server and shamash-web, together with their design notes.
  • Atmospheric sky_color — the colour of the sky from the ground
    • Single-scattered starlight through the Rayleigh column, von Kries adapted to the local star. Earth reads #788CC4; a 90 atm column washes out to near-white, which is what a saturated column should do.

2026.0.4

  • Stateless HTTP API (FastAPI), so a web frontend can do what -w does — now shamash-server. What it needed from the engine stayed here:
    • Phase 0: make the package survive outside its source checkout
      • Resolve version/name/description without reading pyproject.toml at import
      • Ship data/default.yml as declared package data
      • Stop the exporter crashing on optional fields a world leaves out
      • Validate a world file's units against the dimension the field declares
      • Log normal conditions at DEBUG instead of ERROR
    • Request-scoped god registry (contextvars)
    • Worldloader.loads/load_data/to_dict seams, JSON and YAML in/out
    • Console entry point shamash

v2026.0.3

  • Refactor and Introduce Atmospheric model
    • Maybe even in naturedynamics, taking atmospheric models from omniastro
  • Introduce Plant Color and Biosphere Module
    • Based on Star and Atmosphere
  • Geomagnetic Dynamics/ Solarmagnetic Dynamics
  • Make Habitability not true or false but a probability scale, taking into account:
    • Class of the Star and Brightness
    • Calculation Method relative habitability to how far the planet is away

v2026.0.2

Settled on name for project: shamash.

  • Rename Project
  • Refactor export/import handling
    • do not use pprint, but format in cli as well
    • use flag for cli dump
  • Unify Units handling, refactor bad claude code
    • Every function should have their units as metadata with them - decorators?
    • Distances in space are AU/Parsecs/Lightyears
  • Fix main_star_apparent_size=<error: division by zero>
  • Improve logging
    • Add a flag to write to a log file
  • Star Class Calculation
  • Add Star Classes as Enums, maybe directly into naturedynamics
  • Fix Hillsphere Calculation and Inner/Outer Moon Zone Error
  • Also push naturedynamics when pushing worldcreator
    • As of 2026-09-01 naturedynamics is no longer a submodule; it lives at ~/Dev/projects/naturedynamics and is installed into the venv.
    • Release Versioning and Cycle ?
  • Add Horizon Distance
  • Add proper error handling
  • Add a flag to init a new world yaml file
    • shamash init new_world
  • Add Flag to point to yaml world file
  • Make color calculation refined and add Hexcolor support

v2026.0.1

Finally, first working version finished, shamash.

Backlog

  • Add Habitable Zone Shift Factor for planets, to shift habitable zone bonds,
    • regarding how many greenhouse gases exist
    • Axial Tilt
    • Orbital Period
  • Make Apparent Brightness, etc a function that can be calculated at any given point of the orbit.
  • Add LSol as Unit for Luminosity
  • Change ASol to AU
  • Decide what log level a caller's mistake deserves
    • AppError logs at ERROR on construction. Over HTTP every 422 is the client's bad world, not a server incident, so a busy API fills the error log with things no operator can act on — but the same exception from the CLI, against a world file you just edited, is worth an ERROR. One class, two contexts; Phase 0 already established log_level= as the mechanism.
  • Containerise — done under 2026.1.0 above, once unblocked on 2026-09-01 by the package no longer reading pyproject.toml at import, shipping its seed world as package data, and running (CLI and API both) from a wheel with no source tree.
  • make venv pins --python 3.13 while the venv in use is 3.14.6
    • Pinned to 3.14, what everything is verified on. It also gained --seed: without pip in the venv, make install could not run on a fresh one. And its uv pip install -r requirements.txt now names venv/bin/python: uv looks for .venv or an activated env, so it failed unless venv was activated.

Metadata

Release files for shamash-core 2026.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for shamash-core 2026.3.1
File Size Uploaded
shamash_core-2026.3.1.tar.gz 151.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for shamash-core 2026.3.1
File Interpreter ABI Platform
shamash_core-2026.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 336.2 kB

Release files / shamash_core-2026.3.1.tar.gz

Download URL shamash_core-2026.3.1.tar.gz
Size 151.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b75d4084d980ae4b202e79da141bc66a5cc123992469ae1ee8304e11fed360e7
BLAKE2b-256 checksum
How to use checksums
7efbe7035e4fc6a5429fde72b0bc024cd4f9606da1938e9179efc27147d998e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / shamash_core-2026.3.1-py3-none-any.whl

Download URL shamash_core-2026.3.1-py3-none-any.whl
Size 185.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e06cfc5bf175cecfcf50b923d1aef811c1aa40b4ec4561bb4defdd799796ef84
BLAKE2b-256 checksum
How to use checksums
4bdbbab954211caafca6c417403254da994a6abe189be1017d0dec8e751adff0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

2026.3.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page