Skip to content

Schema Reference

Every .randsum.json spec conforms to the JSON Schema at https://randsum.dev/schemas/v1/randsum.json (referenced via $schema). This page documents every field.

{
"$schema": "https://randsum.dev/schemas/v1/randsum.json",
"name": "Blades in the Dark",
"shortcode": "blades",
"version": "1.0.0",
"game_url": "https://bladesinthedark.com",
"srd_url": "https://bladesinthedark.com/basics"
}
Field Type Required Description
$schema string Yes JSON Schema URL for validation
name string Yes Human-readable game name
shortcode string Yes Package identifier — used for subpath exports (@randsum/games/{shortcode}) and generated filenames
version string Yes Spec version (semver)
game_url string No Official game website
srd_url string No System reference document URL

Named dice pool definitions. Each pool describes a type of die used by the game.

"pools": {
"actionDice": { "sides": 6 },
"hope": { "sides": 12 },
"fear": { "sides": 12 }
}
Field Type Required Description
sides number Yes Number of sides on the die

Pools are referenced elsewhere in the spec via { "$ref": "#/pools/{poolName}" }.

Lookup tables that map numeric results to string outcomes. Each table has an array of ranges.

"tables": {
"coreMechanic": {
"ranges": [
{
"poolCondition": { "countWhere": { "operator": "=", "value": 6 }, "atLeast": 2 },
"result": "critical"
},
{ "exact": 6, "result": "success" },
{ "min": 4, "max": 5, "result": "partial" },
{ "min": 1, "max": 3, "result": "failure" }
]
}
}

Each entry in the ranges array matches a roll total (or pool condition) to a result string. Ranges are evaluated in order — the first match wins.

Field Type Description
result string The outcome string returned when this range matches
exact number Matches when the total equals this value
min number Matches when the total is at least this value (used with max)
max number Matches when the total is at most this value (used with min)
poolCondition object Matches based on individual die values instead of the total

Inspects the raw die values before modifiers, not the total. Used for mechanics like Blades criticals (two or more 6s).

"poolCondition": {
"countWhere": { "operator": "=", "value": 6 },
"atLeast": 2
}
Field Type Description
countWhere object Condition to test each die: { "operator": "=" | ">" | "<" | ">=" | "<=", "value": number }
atLeast number Minimum number of dice that must satisfy countWhere

Named outcome definitions that can be referenced by $ref. An outcome either delegates to a table or defines its own inline ranges.

"outcomes": {
"coreMechanicOutcome": { "tableLookup": { "$ref": "#/tables/coreMechanic" } },
"desperateActionOutcome": {
"ranges": [
{ "exact": 6, "result": "success" },
{ "min": 4, "max": 5, "result": "partial" },
{ "min": 1, "max": 3, "result": "failure" }
]
}
}
Field Type Description
tableLookup { "$ref": string } Reference to a named table
ranges array Inline range entries (same format as tables ranges)

The core of the spec. Defines inputs, dice configuration, modifiers, resolution, outcome determination, and conditional overrides.

Defines what the caller passes to the game’s roll() function. Each key becomes a property on the input object (or a positional argument for single-input games).

"inputs": {
"rating": {
"type": "integer",
"minimum": 0,
"maximum": 4,
"default": 1,
"description": "Action rating (0-4)"
},
"rollingWith": {
"type": "string",
"enum": ["Advantage", "Disadvantage"],
"optional": true
},
"crit": {
"type": "boolean",
"optional": true,
"description": "When true, check for natural 1 and natural 20"
}
}
Field Type Description
type "integer" | "string" | "boolean" Input value type
minimum number Minimum value (integer inputs only)
maximum number Maximum value (integer inputs only)
default any Default value when the input is omitted
optional boolean Whether the input can be omitted (no default needed)
enum string[] Allowed values (string inputs only)
description string Human-readable description

Codegen produces validateRange() and validateFinite() calls for integer inputs with minimum/maximum, and enum validation for string inputs with enum.

Configures the dice to roll. For single-pool games.

"dice": {
"pool": { "$ref": "#/pools/actionDice" },
"quantity": { "$input": "rating" }
}
Field Type Description
pool object Either { "$ref": "#/pools/{name}" } or inline { "sides": number }
quantity number | { "$input": string } Number of dice, or dynamic from an input value

For multi-pool games (e.g., Daggerheart’s hope/fear dice). Each key names a pool that can be referenced in resolve and details.

"dicePools": {
"hope": { "pool": { "sides": { "$input": "amplifyHope", "ifTrue": 20, "ifFalse": 12 } } },
"fear": { "pool": { "sides": { "$input": "amplifyFear", "ifTrue": 20, "ifFalse": 12 } } }
}

Dynamic sides use { "$input": string, "ifTrue": number, "ifFalse": number } for boolean-conditional values.

Pools that are only rolled when a condition is met. Used for Daggerheart’s advantage/disadvantage extra die.

"conditionalPools": {
"advantage": {
"condition": { "input": "rollingWith", "operator": "=", "value": "Advantage" },
"pool": { "sides": 6 },
"arithmetic": "add"
}
}
Field Type Description
condition object When to include this pool (same format as when conditions)
pool object Die definition ({ "sides": number })
arithmetic "add" | "subtract" How to combine this pool’s result with the total

Array of modifier operations applied to the dice. Modifiers run in the order specified.

"modify": [
{ "keepHighest": 1 },
{ "add": { "$input": "modifier" } }
]

Available modifiers:

Modifier Value Description
keepHighest number Keep the N highest dice
keepLowest number Keep the N lowest dice
add number | { "$input": string } Add to the total

The { "$input": string } form reads the value from a named input at runtime.

How the roll total is determined.

Value Description
"sum" Sum all dice values after modifiers (most games)
{ "comparePoolHighest": ... } Compare highest values across named pools (Daggerheart)
{ "remoteTableLookup": ... } Look up results from an external table (Salvage Union)
"resolve": {
"comparePoolHighest": {
"pools": ["hope", "fear"],
"ties": "critical_hope",
"outcomes": { "hope": "hope", "fear": "fear" }
}
}
Field Type Description
pools string[] Named pools to compare
ties string Result string when pools tie
outcomes object Maps winning pool name to result string

Tables are fetched at build time and baked into the generated code. No network calls at runtime.

"resolve": {
"remoteTableLookup": {
"url": "https://salvageunion.io/schema/roll-tables.json",
"find": {
"field": "name",
"input": "tableName",
"errorMessage": "Invalid table name: \"${value}\""
},
"tableField": "table",
"resultMapping": {
"key": { "$lookupResult": "key" },
"label": { "$lookupResult": "result.label", "fallback": { "$lookupResult": "key" } },
"description": { "$lookupResult": "result.value" },
"tableName": { "$input": "tableName" },
"table": { "$foundTable": "table" },
"roll": { "expr": "total" }
}
}
}

Modifiers applied after resolution. Used when the resolve step produces a non-sum result (e.g., Daggerheart applies the stat modifier after comparing hope/fear pools).

"postResolveModifiers": [{ "add": { "$input": "modifier" } }]

Determines the string outcome from the numeric total. Either a $ref to a named outcome or inline ranges.

// Reference a named outcome
"outcome": { "$ref": "#/outcomes/coreMechanicOutcome" }
// Inline ranges
"outcome": {
"ranges": [
{ "min": 10, "max": 27, "result": "strong_hit" },
{ "min": 7, "max": 9, "result": "weak_hit" },
{ "min": -11, "max": 6, "result": "miss" }
]
}

Games without an outcome field return a numeric result (like D&D 5e).

Additional structured data attached to the GameRollResult.details field. Each key becomes a property on the details object.

"details": {
"criticals": {
"when": { "input": "crit" },
"value": {
"isNatural1": {
"$dieCheck": { "pool": 0, "field": "final", "die": 0, "operator": "=", "value": 1 }
},
"isNatural20": {
"$dieCheck": { "pool": 0, "field": "final", "die": 0, "operator": "=", "value": 20 }
}
}
}
}

Detail values can be:

Form Description
{ "$input": string } Value from a named input
{ "$input": string, "default": any } Input value with a default
{ "$pool": string, "field": "total" } Total from a named pool
{ "$conditionalPool": string, "field": "total" } Total from a conditional pool
{ "$dieCheck": object } Check a specific die value
{ "expr": string } Expression reference (e.g., "diceTotal", "total")
{ "when": object, "value": object } Conditional detail — only present when input is truthy

Inspects a specific die in the roll results.

Field Type Description
pool number Pool index (0-based)
field "final" | "initial" Check the post-modifier or pre-modifier value
die number Die index within the pool (0-based)
operator "=" | ">" | "<" | ">=" | "<=" Comparison operator
value number Value to compare against

Conditional overrides that change the roll behavior based on input values. Each entry has a condition and an override that replaces specific fields.

"when": [
{
"condition": { "input": "rating", "operator": "=", "value": 0 },
"override": {
"dice": { "pool": { "$ref": "#/pools/actionDice" }, "quantity": 2 },
"modify": [{ "keepLowest": 1 }],
"outcome": { "$ref": "#/outcomes/desperateActionOutcome" }
}
}
]
Field Type Description
input string Input field to check
operator "=" | "!=" | ">" | "<" | ">=" | "<=" Comparison operator
value any Value to compare against

An override can replace any of dice, dicePools, modify, resolve, or outcome. Only the fields specified are overridden — unmentioned fields keep their default values.

When multiple when entries match, they are evaluated in order and the first match is used.

Specs use JSON Pointer-style references ($ref) to avoid duplication. References always point within the same spec file using the #/ prefix.

{ "$ref": "#/pools/actionDice" } // resolves to pools.actionDice
{ "$ref": "#/tables/coreMechanic" } // resolves to tables.coreMechanic
{ "$ref": "#/outcomes/coreMechanicOutcome" } // resolves to outcomes.coreMechanicOutcome

For a complete, working spec, see the Salvage Union source — the richest spec in the ecosystem with multiple roll tables, range-based outcomes, and dynamic table selection — or any other game spec in the packages/games/ directory.