Skip to content

Configuration

VoLCA is configured through a TOML file, typically named volca.toml. Pass it to any command with --config volca.toml.

Top-level scalar fields (such as geographies) must come before any [section] or [[array-of-tables]] header. After a header, every key is parsed as a member of that section – so a top-level key written lower down becomes server.geographies instead, and no longer means anything. Startup warns about the keys nothing reads, naming each one, which is how you find that kind of mistake. It does not reach inside a table whose keys you choose yourself, such as locationAliases or a scoring set’s variables, so a stray key there is still lost quietly.

How you run VoLCAWhere its configuration comes from
volca --config FILE ...The file you name. Relative paths inside it resolve against the file’s own directory, not your working directory
volca server with no --configBuilt-in defaults: port 8080, loopback, no databases
The Docker image/app/volca.toml inside the image, unless you mount your own and point --config at it
The desktop appThe bundled file, with your fragment appended (see below)
A hosted engineWritten for you; [hosting] is what its operator sets

The file is read once, at startup. A change takes effect when the engine restarts: there is no reload. On a hosted engine you do not restart anything yourself, the idle timeout does it.

Three settings can be overridden without touching the file, which is what a container or a service unit uses. Each override wins over the file, except where noted:

SettingOverridden by
[server] port--port
[server] password--password. VOLCA_PASSWORD is read only when neither the flag nor the file sets one, so it cannot rotate a password written in the file
Which databases load at startup--load name1,name2

Two environment variables act on paths rather than settings. VOLCA_DATA_DIR is the engine’s write root, where uploaded databases and their caches are kept. It also redirects any reference-data path that begins with data/ to that directory, which is how a configuration of your own can point into the data bundle a release ships. VOLCA_URL tells the command-line client which server to talk to, instead of deriving one from [server].

The desktop app ships its own volca.toml inside the application bundle; do not edit that copy. To extend it, create a volca.toml in the app’s data directory:

PlatformPath
Linux~/.local/share/com.volca.desktop/volca.toml
macOS~/Library/Application Support/com.volca.desktop/volca.toml
Windows%APPDATA%\com.volca.desktop\volca.toml

At every launch the app appends this fragment to a copy of the bundled configuration, written beside it as .volca-assembled.toml, and starts the engine on that copy. The fragment can only add entries: [[classification-presets]] of your own, [[databases]] or [[methods]] read from disk, or extra reference data files. Use absolute paths; a relative path resolves against the data directory above.

Three things to know:

  • The fragment must start with a [section] or [[section]] header, and every line in it has to sit under one. A top-level key cannot go in a fragment at all: appended below the bundled part’s sections, TOML would read it as a member of the last one. The app refuses a fragment that opens with a bare key, but that check reads the first line only, so a bare key further down is still swallowed.
  • Pick names that the bundled configuration does not already use. Startup refuses a duplicated database, method-collection or preset name. Labels are not checked, so two presets may share one and the dropdown will show it twice.
  • A TOML mistake stops the engine and the loading screen shows the parse error. Its line numbers refer to .volca-assembled.toml, whose tail is your fragment: open that file to find the line.

Three top-level keys name single CSV files. geographies has a built-in default, the hierarchy the engine was built with, and a file replaces it. The other two are optional; without one, the engine does without that table rather than substituting a default.

geographies = "data/geographies.csv" # region codes, display names, parents
chem-synonyms = "data/chem_synonyms.csv" # PubChem snapshot for the mapping suggester
substance-edges = "data/substance_edges.csv"
KeyWhat it does
geographiesThe location hierarchy a regionalized characterization factor cascades through, the geography a cross-database link resolves against, and the region names and parents list_geographies reports. Unset, the built-in hierarchy applies
chem-synonymsWidens the flow-mapping suggester’s recall. See Flow Mapping Audit
substance-edgesTyped correspondences between substances, used when a flow and a characterization factor name the same thing differently
[server]
port = 8080
host = "127.0.0.1" # the interface to listen on
password = "secret" # optional - guards /api/ and /mcp
name = "lab-archive" # optional - how this server introduces itself over MCP
FieldDefaultDescription
port8080HTTP port. --port overrides it; --port 0 takes a free port from the operating system, on loopback
host127.0.0.1The interface to listen on. The default answers this machine and nothing else, which is what leaving the password unset assumes. "0.0.0.0" answers the network over IPv4, "::" over IPv6, and a specific address answers on that interface alone
password–If set, every request to /api/ and /mcp must carry it: Authorization: Bearer <password>, HTTP Basic, or the session cookie the login page sets. Static files and the login page stay public. --password wins over this field; VOLCA_PASSWORD is only read when neither is set
name–If set, the MCP handshake announces it, so an assistant connected to several VoLCA servers can say which one answered

Each database is a [[databases]] entry. The format is detected from the file extension and contents – there is no format field.

[[databases]]
name = "ecoinvent" # identifier used in the --db flag
path = "/data/ecoinvent-3.10" # directory or archive with the data files
load = true # load at startup
[[databases]]
name = "agribalyse"
path = "/data/agribalyse4.csv"
load = true
depends = ["ecoinvent"] # cross-database linking (flows resolved against ecoinvent)
FieldDefaultDescription
namerequiredUnique identifier; used as --db NAME in the CLI
pathrequiredDirectory or file path to the data
displayNamenameHuman-readable name
description–A sentence about this database, shown where it is listed
loadfalsetrue loads it at startup, along with everything it depends on. false leaves it configured but not in memory
defaultfalseMarks this as the default database. At most one may
dependsnoneDatabase names to resolve unlinked suppliers against
deletablefalseWhether this database may be deleted at runtime. Uploaded databases are deletable; one named here is background data the whole installation shares, so it is not unless you say so
allocationdeclaredHow a process with several products is divided between them. declared keeps the shares the source states. wet mass weighs each product by the wet mass its line states, or by its own amount where that amount is already a mass. dry mass weighs by a stated dry mass only, since nothing in a wet kilogram says how much of it is water. The space in the two-word values is part of them. A process the key cannot weigh is not divided at all and has no column in the matrix, the same refusal a process stating no share gets; the load says how many there were, names ten of them, and why
geography_policyglobalHow a supplier is chosen when none is available in the region asked for: exact refuses to substitute, parent walks up the geography hierarchy, global falls back to a global dataset
locationAliasesnoneA table of corrections applied to this database’s location codes, written wrong = "right"
patchesnoneCorrections to the amounts this database states, applied as it is read. See Patching the amounts a database states
[[databases]]
name = "regional-study"
path = "/data/study.csv"
geography_policy = "parent"
[databases.locationAliases]
"FR " = "FR"
"Europe (RER)" = "RER"

A source file sometimes states more than the study you read it for models: a substance withdrawn from use, a treatment left out on purpose. Rather than rewrite the file, declare the correction in the configuration. A [[databases.patches]] entry belongs to the [[databases]] entry written above it, so keep the two together:

[[databases]]
name = "orchards"
path = "/data/orchards.csv"
[[databases.patches]]
description = "the preservative these poles are no longer treated with"
match = { product-name-contains = "wooden poles", flow-name-contains = "creosote" }
set-value = 0.0
[[databases.patches]]
description = "half the dose on French fields"
match = { location = "FR", flow-name = "Acetamiprid" }
scale = 0.5

A patch picks exchanges by the process they belong to and by the flow exchanged. Every selector it sets must match, and it must set at least one:

SelectorMatches
activity-name-containsPart of the activity’s name, whatever the letter case
product-name-containsPart of the name of the product the process makes, whatever the letter case
locationThe process’s location, exactly as the source writes it
flow-nameThe name of the flow exchanged, exactly
flow-name-containsPart of that name, whatever the letter case

A part of a name left blank is refused at startup, since every name contains it. A SimaPro export often leaves the activity name empty and writes the whole designation in the product name, so product-name-contains is the selector that reads the same across formats.

The patch then multiplies the amount (scale) or replaces it (set-value), exactly one of the two. It reaches the inputs, the emissions, the waste sent to treatment and the avoided products of a process, never the rows saying what the process makes: those define the unit every other amount is stated per. A set-value is written in the unit the engine records the row in, which for SimaPro CSV and Brightway Excel is the reference unit of its dimension (a row written in grams is recorded in kilograms). A process with several products is divided before patches apply, so a set-value lands whole on each of the processes it was divided into.

The load says how many exchanges each patch touched, naming the patch by its description (or, without one, by its selector), and warns about a patch that touched none, which is most often a misspelt name. A database whose list of patches changed is rebuilt from its source rather than read from its cache. A database derived or copied from a patched one reads the same files, and takes its patches from it.

FormatExtension / structure
EcoSpold 2Directory or .7z/.zip of .spold files
EcoSpold 1.xml file (older format)
SimaPro CSV.csv or .csv.zip file
ILCDDirectory with processes/ and flows/ subdirectories
Brightway ExcelSingle .xlsx file (Brightway/bw2io layout)

A [[methods]] entry is a collection of characterization methods read from one file or directory. Four shapes are accepted: an ILCD method package (directory or .zip of XML), a SimaPro CSV method export, a columnar method CSV, or an openLCA ImpactCategory JSON-LD document.

[[methods]]
name = "EF3.1"
path = "/data/EF3.1" # directory or .zip of ILCD method XML, or a SimaPro CSV export
active = true # default true
description = "JRC ILCD EF 3.1"
global-methods = ["Land use"]
FieldDefaultDescription
namerequiredIdentifier for this collection
pathrequired, except for a built-in collectionFile or directory holding the methods
activetruefalse keeps the collection configured but unloaded
description–A sentence about this collection
global-methodsnoneNames of methods in this collection whose location-specific factors are dropped, so every flow falls back to the method’s own unlocated factor. A method whose factors are all region-tagged is left with none, and scores zero

A collection can also carry single-score definitions and corrections to individual factors, in [[methods.scoring]] and [[methods.patches]]. Both are described on their own page: Single score.

One collection is built into the engine and loaded beside the ones a configuration lists: plain-indicators, which counts raw physical quantities through the supply chain (land occupied, water used, fossil CO2, methane, primary energy, waste heat, cadmium), every factor equal to 1. An entry with that name and a path replaces it with a file. An entry with that name and no path keeps it, and can switch it off or give it single scores and corrections:

[[methods]]
name = "plain-indicators"
active = false # switched off

Any other entry without a path is refused at startup.

Three arrays name CSV files that help the engine recognise the same thing under different names, and one bridges units. All four tables are built into the engine and active by default, as Default flow synonyms, Default compartment mapping, Default units and Default energy densities. A configuration that says nothing about a kind runs on the built-in table; one that lists entries for a kind gets exactly what it lists, so a file of your own stands beside the built-in table only when both are named. Name the built-in with a path to replace it with a file, or with no path and active = false to switch it off:

[[flow-synonyms]]
name = "my-synonyms"
path = "/data/my-synonyms.csv"
[[flow-synonyms]]
name = "Default flow synonyms" # kept beside the file above
[[compartment-mappings]]
name = "Default compartment mapping"
path = "/data/compartments.csv" # replaced by a file of your own
[[units]]
name = "Default units"
path = "/data/units.csv"
[[energy-densities]]
name = "Default energy densities"
active = false # switched off

All four take the same fields: name, path, active (default true) and an optional description. An entry with no path can only name the built-in table of its array; any other name without a path is refused at startup.

ArrayWhat it does
[[flow-synonyms]]Pairs of names for the same biosphere flow, so a database and a method that spell it differently still meet. One name1,name2 pair per row, with an optional direction, CAS number and note
[[compartment-mappings]]Which compartments are one place written two ways (same), and which place a flow reads when the method’s collection never writes its own (if_absent). See Flow Mapping
[[units]]Unit conversion factors
[[energy-densities]]The density bridge: how much energy a kilogram of a fuel carries, and how much volume it takes. Without it, a factor written per MJ or per m³ can never meet a flow measured by mass, and those categories score zero without saying so

A unit is read against the spellings its table holds, and the case is part of the spelling. An exact spelling is taken in silence; a spelling that differs from exactly one entry by case alone is taken too, and the load says which entry it was read as, so KWH loads and reports that it is written kWh; and a spelling that could equally be two entries stops the load and names both, since nothing in the data says which is meant and mj against a table holding mJ and MJ is a billion apart either way. An unknown unit warns and lets the load continue. A table that spells one unit twice is refused at startup rather than keeping one row and dropping the other in silence.

[hosting] is for an engine serving people who did not configure it. It is the only section whose settings are refusals rather than data, and every one of them is reported by GET /api/v1/hosting, so a client can explain the limit before a request runs into it.

[hosting]
read_only = true
read_only_message = "This shared engine is read-only. Create your own to upload."
max_uploads = 2
max_upload_mb = 100
max_loaded_uploads = 1
api_access = true
upgrade_upload = "Create your own engine to upload a database of your own"
FieldDefaultDescription
read_onlyfalsetrue refuses everything that changes state: loading, unloading, uploads, deletes, edits, and the shutdown endpoint. Queries still answer
read_only_messagethe built-in sentenceThe refusal in your own words, repeated on every surface that refuses
max_uploads-1How many databases may be uploaded. -1 is unlimited, 0 disables uploading
max_upload_mb100Size ceiling per upload, in megabytes. -1 is unlimited, 0 disables uploading. The default applies once [hosting] exists; with no [hosting] at all there is no ceiling
max_loaded_uploads-1How many uploaded databases may be in memory at once. A machine limit rather than an offer limit: loading one more asks you to unload another
api_accesstrueReported for whatever fronts the engine to enforce. The engine itself refuses nothing on it, so on a standalone server this setting only describes an intention
upgrade_upload–What to say instead of a bare refusal when an upload is refused
upgrade_api–The same, when programmatic access is refused
upgrade_vm_size–The same, when the machine is short of memory

An empty message means the engine falls back to its own sentence, which says what would clear the way rather than pointing at a plan to buy.

Named filter shortcuts that appear in the Activities search dropdown. Each preset applies one or more { system, value, mode } classification filters.

[[classification-presets]]
name = "food"
label = "Food products"
description = "Everything under the two food branches of ISIC"
filters = [
{ system = "ISIC rev.4 ecoinvent", value = "01", mode = "contains" },
{ system = "ISIC rev.4 ecoinvent", value = "10", mode = "contains" },
]

label defaults to name, and mode defaults to "exact"; the other value is "contains".

[server]
port = 8080
host = "127.0.0.1"
[[databases]]
name = "ecoinvent"
path = "/data/ecoinvent-3.10-cutoff"
load = true
[[databases]]
name = "agribalyse"
path = "/data/agribalyse4.csv"
load = true
depends = ["ecoinvent"]
[[methods]]
name = "EF3.1"
path = "/data/EF3.1"
active = true