The Universe-as-Code format

An Axiom universe can be defined entirely as a tree of plain-text files (TOML and Markdown). The text is the source of truth; the SQLite .db the engine actually runs on is a compiled cache, regenerated whenever the source changes. This makes universes diff-able, reviewable and shareable on Git, like code.

$ axiom compile my-world/        # source tree -> .db cache
$ axiom decompile World.db out/  # existing .db -> source tree
$ axiom dev my-world/            # watch & hot-recompile while you edit

Only the definition of the universe lives in the source tree (entities, rules, lore, map…). Player saves are runtime data, stored separately — see Saves, rewind and sharing.

Layout

my-world/
├── universe.toml            # required: metadata, narration, calendar
├── stats/
│   └── definitions.toml     # stat definitions (optional)
├── entities/
│   ├── hero.toml            # one file per entity
│   └── innkeeper.toml
├── rules/
│   └── poison.toml          # one file per rule
├── locations/
│   └── map.toml             # locations + connections
├── lore/
│   ├── _global_lore.md      # referenced from universe.toml
│   └── history/origins.md   # every other .md becomes a lore-book entry
├── events/
│   └── eclipse.toml         # scheduled events
├── items/
│   └── rusty_sword.toml     # item definitions
├── setup/
│   └── questions.toml       # story-setup questionnaire
└── .axiom-cache/            # compiled cache (generated; don't commit)
    └── universe.db

Every folder except universe.toml is optional. Files named _index.toml are ignored, and so is anything under .axiom-cache/ and .git/.

universe.toml

[meta]
name = "The Clockwork City"

[narrative]
system_prompt = "You are the narrator of a noir city of clockwork gods."
# Long texts can live in their own file instead of inline:
global_lore_file = "lore/_global_lore.md"     # or: global_lore = "…"
first_message_file = "lore/_first_message.md" # or: first_message = "…"
world_tension_level = "simmering unrest"

[calendar]               # optional custom calendar
minutes_per_hour = 60
hours_per_day = 24
days_per_month = [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30]
month_names = ["Frostfall", "Embertide"]  # … one name per month
start_day = 1
start_hour = 8
start_minute = 0

[companion]              # optional Companion mode defaults
enabled = true
hero_id = "hero"

[extra]                  # free-form keys, preserved verbatim
my_custom_key = "value"

global_lore / first_message may be given inline or via a *_file path; referenced files are excluded from the lore book.

Entities — entities/*.toml

One file per entity:

entity_id = "innkeeper"          # required, stable identifier
entity_type = "npc"              # default: "npc"
name = "Marla the Innkeeper"
description = "A weathered woman who hears everything."
is_active = true                 # default: true

[stats]                          # initial stat values (strings)
Health = "100"
Location = "tavern"
Mood = "wary"

Stat definitions — stats/definitions.toml

[[definitions]]
stat_id = "Health"
name = "Health"
description = "Hit points."
value_type = "numeric"           # default: "numeric"
parameters = { min = 0, max = 100 }

Rules — rules/*.toml

One file per rule. Conditions and actions are stored as JSON-compatible structures and evaluated by the engine each turn:

rule_id = "poison_tick"
priority = 10                    # default: 0
target_entity = "*"              # default: "*" (any entity)

[conditions]
stat = "Poisoned"
equals = "true"

[[actions]]
type = "modify_stat"
stat = "Health"
delta = -5

Locations — locations/map.toml

[[locations]]
location_id = "tavern"
name = "The Rusty Cog"
scale = "poi"                    # e.g. "poi", "district", "city", "region"
parent_id = "old_town"           # optional hierarchy
description = "Smoke, gears and cheap gin."
x = 12.5
y = 4.0

[[connections]]
source_id = "tavern"
target_id = "market"
distance_km = 1

Lore book — lore/**/*.md

Every Markdown file under lore/ (recursively) becomes a lore-book entry, except files referenced from universe.toml. An optional TOML frontmatter between +++ delimiters carries the metadata:

+++
entry_id = "origins"
category = "history"
name = "The Origins of the City"
keywords = "clockwork, gods, founding"
+++
Long ago, the first gear was set in motion…

Without frontmatter, the entry_id is derived from the relative path and the name from the file name. The body is preserved byte-for-byte (compile → decompile round-trips are lossless).

Scheduled events — events/*.toml

Events fire when the in-game clock reaches trigger_minute:

event_id = "eclipse"
trigger_minute = 4320            # in-game minutes from the start
title = "The Brass Eclipse"
description = "The clockwork sun grinds to a halt."

Items — items/*.toml

item_id = "rusty_sword"
name = "Rusty Sword"
description = "It has seen better centuries."
category = "weapon"              # default: "misc"
weight = 3.5
rarity = "common"

Story setup — setup/questions.toml

Questions asked when a new game starts:

[[questions]]
setup_id = "origin"
question = "Where do you come from?"
type = "choice"                  # default: "text"
options = ["The Foundry", "The Undercity", "Outside the walls"]
max_selections = 1
priority = 0

Compilation and the cache

axiom compile my-world/ hashes the whole source tree and writes the compiled database to my-world/.axiom-cache/universe.db, plus the hash, so unchanged sources are not recompiled (--force overrides this; -o chooses another output path). During authoring, axiom dev my-world/ watches the tree and recompiles on every change.

To share a universe, pack it into a single .axiom archive:

$ axiom pack my-world/ MyWorld.axiom
$ axiom import MyWorld.axiom     # on the other side (v1 archives work too)

From Python, the same operations are available in axiom.compile, axiom.decompile, axiom.package and axiom.dev.