Skip to main content

Lua v4 Script Configuration

Lua v4 configuration is static bundle metadata plus a validated value file. A bundle declares a small flat schema without executing Lua. Hexis validates the schema before a run, captures one complete configuration snapshot when that run starts, and gives Lua fresh table copies through hexis.config.snapshot().

The backend and Lua read call are implemented in the source foundation. They remain non-executable in these docs until a named distributed build passes controlled acceptance. The current Galatea UI adapter still has no customer settings editor, so the persisted update path is not yet exposed through that module's settings panel.

Exact manifest dialect

configSchemaVersion is a positive integer owned by the bundle. The configSchema root has exactly four fields:

{
"type": "object",
"additionalProperties": false,
"required": ["route", "competition_mode", "max_azaleas"],
"properties": {
"route": {
"type": "string",
"title": "Fig Tree Route",
"default": "official:Galatea_Foraging",
"format": "hexis-route",
"maxLength": 160
},
"competition_mode": {
"type": "boolean",
"title": "Agatha Contest Mode",
"default": true
},
"max_azaleas": {
"type": "integer",
"title": "Max Azaleas Between Trees",
"default": 6,
"minimum": 0,
"maximum": 15,
"step": 1
}
}
}

type must be object, and additionalProperties must be false. A schema has at most 32 properties. Each property name must match [a-z][a-z0-9_]{0,63}. required must list every property exactly once, with no missing, duplicate, optional, or undeclared names. An empty schema uses empty required and properties values.

Every field requires a nonblank title of at most 80 characters and a valid default. Only these scalar types and keywords exist:

TypeRequired keywordsOptional keywords
booleantype, title, defaultnone
integertype, title, default, minimum, maximumstep
numbertype, title, default, minimum, maximumstep
stringtype, title, default, maxLengthminLength, enum, format

Unknown or missing keywords reject the bundle. Arrays, nested configuration objects, nulls, descriptions, and undeclared JSON Schema features are not part of this dialect.

For numeric fields, the minimum, maximum, default, and optional step must be finite. The minimum cannot exceed the maximum. A step must be positive, and both defaults and saved values must align to it from the minimum. Integer schema values must be exact integers no larger in magnitude than 9,007,199,254,740,991.

For strings, maxLength is an integer from 1 through 4,096. Optional minLength is from 0 through maxLength. An enum contains 1 through 64 unique, nonempty strings, and its default must be one of them. enum and format cannot be combined.

The only format is hexis-route. It limits maxLength to 160 and accepts this shape:

[a-z0-9._-]+:[A-Za-z0-9._/-]+

All defaults are validated by the same rules as saved values. A bundle with an invalid schema never starts Lua.

One immutable snapshot per run

The public call takes no arguments and requires no capability:

local config = hexis.config.snapshot()
local selected_route = config.route

Before creating the run, Hexis loads one complete validated snapshot for that bundle's stable script ID and configSchemaVersion. The host keeps that snapshot immutable for the run. Every call to hexis.config.snapshot() returns a fresh mutable Lua table copied from it. Changing a returned table cannot change the run snapshot, another call, the stored settings, or another run.

There is no Lua setter, live config event, shared mutable table, capability, or filesystem access. A successful settings update applies to the next run. It does not alter a script that is already running. A script that wants new values must stop and start a new run.

Stable identity and persistence

Configuration is keyed by the manifest's stable ScriptId, not its display name, version, file hash, installation origin, or UI position. A script ID uses lowercase namespace:name; each side matches [a-z0-9][a-z0-9._-]*.

The production file path is:

<Fabric config directory>/hexis/script-v4/<namespace>/<name>.json

For hexis:galatea-fig, that is <Fabric config directory>/hexis/script-v4/hexis/galatea-fig.json. The file contains exactly:

{
"schemaVersion": 2,
"values": {
"route": "official:Galatea_Foraging",
"competition_mode": true,
"max_azaleas": 6
}
}

The stored file is limited to 64 KiB. values must contain exactly every schema property and only scalar booleans, strings, or numbers accepted by the current schema.

A missing, empty, oversized, malformed, non-object, wrong-version, incomplete, extra-field, or otherwise invalid file produces the schema defaults. Hexis does not rewrite, delete, rename, or automatically migrate that file while reading it. This avoids destroying evidence or replacing an old configuration merely because a new bundle changed its schema. Bundle authors must increment configSchemaVersion when the schema meaning changes. There is no automatic value migration in this foundation. The production store caches its current value by script ID and schema version, so manually editing the JSON while Hexis is running is not a supported update path and is not guaranteed to be observed.

Validated atomic updates

The backend validates the entire candidate before writing. It serializes a sibling .tmp file, flushes the file contents and metadata, then attempts an atomic replace of the final JSON. If the filesystem does not support atomic move, it falls back to a normal replacement. The in-memory value changes only after the replacement succeeds. A failed write keeps the previous cached snapshot and removes the temporary file on a best-effort basis.

This is strong protection against a partially written JSON file, but it is not an absolute power-loss guarantee. The implementation does not fsync the parent directory after the rename, and the non-atomic fallback is necessarily weaker. Documentation and UI must not claim stronger durability than the underlying filesystem provides.

Configured official bundles

Galatea config schema version 2 has exactly three required settings:

FieldExact contractCurrent meaning
routehexis-route string, default official:Galatea_Foraging, maximum length 160Selects the route used for both tree and azalea entry filtering
competition_modeboolean, default trueEnables the bounded azalea and Tadpole detours; false runs the tree scheduler only
max_azaleasinteger 0 through 15, default 6, step 1Caps successful azalea harvest actions between successful nonempty trees; 0 disables azalea detours

The name competition_mode does not imply contest detection, score tracking, or automatic stopping at a contest maximum. It is only the scheduler toggle described above. Galatea reads the config once and combines config.route with the workload's tree or azalea route filter:

local WORKLOAD = hexis.content.pack({name = "workload"})
local CONFIG = hexis.config.snapshot()

local function route_request(filter)
return {
route = CONFIG.route,
kind = filter.kind,
entry_block_types = filter.entry_block_types,
}
end

The hexis.galatea_fig workload is schema version 5. Its tree_route and azalea_route objects contain only kind and entry_block_types; they no longer contain a route ID. This keeps the customer-selected route in one authoritative configuration field instead of duplicating it in validated workload data. Its tree and azalea selectors now contain exact namespaced block IDs mapped to integer priorities, matching the v4 mining request directly. The old pack-owned max_azaleas_between_trees field is also gone; the validated max_azaleas customer setting is authoritative.

The reduced Sven bundle has one required flee_health integer setting. Its default is 10, minimum 5, maximum 20, and step 5. The entrypoint passes that snapshot value to combat.engage.stop_below_health_percent and stops its run on the non-retryable player_low_health result. This is a stop threshold, not an automatic healing or retreat implementation.

The reduced Experiment Table bundle uses config schema version 2 with exactly three required booleans: solve_chrono, solve_ultra, and solve_superpairs. Each defaults to true. The entrypoint reads one snapshot and exits before subscribing or clicking when the already-open experiment's solver is disabled. There is no auto_renew property; renewal and currency spending remain excluded rather than hidden behind a setting.

Marketplace relevance and remaining gates

The implemented content-source boundary can list a bundle's stable ID and strict configuration schema, along with its source, compatibility, and descriptive install state, then prepare one exact selected version. That list-and-prepare seam can be implemented by a future marketplace source without changing the Lua runtime. Marketplace download, install, update, removal, rollback, publisher verification, catalog trust, conflict handling, and settings migration are not implemented yet.

Before configuration is public, a named distributed build still needs live acceptance for the settings editor, successful and failed persistence, restart loading, schema-version mismatch, defaults, and proof that an active run stays unchanged until restart. The backend alone does not prove that UI workflow.