Guide¶
Reference material that doesn’t fit on the front page: how
configuration values are loaded, the order to call functions in, the
full list of environment variables, a full example invenio.cfg, how
the package works internally, and known gaps. For a one-line-per-function
summary with links into the API reference, see the
front page.
Configuration sources and precedence¶
This section is for whoever deploys/operates an instance (sets up
variables, .env, or the process environment) — for how
invenio.cfg itself consumes these values in code, see
Reading configuration variables in invenio.cfg
further down.
Several functions (configure_generic_parameters, configure_ui,
configure_einfra_oidc) pull values from the environment via
load_configuration_variables(). Values are merged from multiple sources,
lowest to highest priority:
A
variablesfile located next toinvenio.cfgitself (i.e. in the Invenio instance directory — found viaINVENIO_INSTANCE_PATHif set, otherwise by walking the call stack for a frame whose filename ends ininvenio.cfg).A
variablesfile in the current working directory (conventionally the repository root you run commands from).A
.envfile in the current working directory (overridesvariables).If
INVENIO_CONFIG_PATHis set, every*.json/*.yaml/*.ymlfile found recursively under that directory, processed in alphabetical path order. Keys are upper-cased and prefixed withINVENIO_if not already prefixed.Actual process environment variables starting with
INVENIO_(highest priority — always wins).
Every value is passed through transform_value(): the literal strings
"True"/"False" become booleans, and anything that parses as JSON
(numbers, [...], {...}, quoted strings) is converted; otherwise the
raw string is kept. This matters when editing variables/.env by hand —
e.g. INVENIO_S3_ACCESS_KEY=aa stays the string "aa", but
INVENIO_OPENSEARCH_USE_SSL=False becomes the Python boolean False, and
INVENIO_SOME_LIST=["a", "b"] becomes an actual list.
Reading configuration variables in invenio.cfg¶
This section is for whoever writes/maintains invenio.cfg or other
configure_*() helpers — for the on-disk/env-var precedence rules an
operator needs to know, see
Configuration sources and precedence
above.
load_configuration_variables() returns a dict-with-attribute-access.
Accessing a missing variable as an attribute (env.INVENIO_SECRET_KEY)
raises AttributeError — so any variable a configure_*() function
reads without a .get(..., default) fallback is effectively required.
Keep this in mind if you write your own configure_*()-style helper:
prefer env.get("INVENIO_FOO", default) for optional settings, and plain
attribute access only for settings the deployment must provide.
override_configuration(env=None) is a separate, opt-in escape hatch: for
every loaded variable named INVENIO_<X>, it strips the INVENIO_ prefix
and sets <X> directly as a config constant in invenio.cfg — i.e. it
lets ops set arbitrary Flask config keys (e.g.
INVENIO_RDM_ARCHIVE_DOWNLOAD_ENABLED=false in variables/.env) without
touching Python code. It is not called automatically by anything else in
this package; call it yourself (typically last, so it wins over everything
set by configure_*() calls above it) if you want that behaviour.
Ordering¶
Because several functions read back constants set by earlier ones, a safe
call order in invenio.cfg is:
initialize_i18n()/initialize_glitchtip()(no dependencies)configure_generic_parameters()(seedsAPP_DEFAULT_SECURE_HEADERS, vocabulary schemes, datastream readers/writers/transformers)configure_ui()(extendsAPP_DEFAULT_SECURE_HEADERS, setsREPOSITORY_NAME)configure_oai()(needsREPOSITORY_NAME)configure_communities(),configure_workflows()(or the low-levelregister_workflow()),configure_cron(),configure_stats(),configure_vocabulary(),configure_datastreams(),configure_jobs(),configure_einfra_oidc(),configure_llm(),add_model()— order among these mostly doesn’t matter, except that anything relying onVOCABULARIES_DATASTREAM_*defaults should still come after step 2.Any plain
CONSTANT = valueoverrides, andoverride_configuration()if used, last — so they win over everything above.
Environment variables¶
Consumed via load_configuration_variables() (see loading order above).
Required unless noted otherwise — accessing a required-but-unset variable
raises AttributeError when invenio.cfg is imported.
Variable |
Used by |
Notes |
|---|---|---|
|
|
Flask |
|
|
fallback for |
|
|
fallback for |
|
|
optional, overrides host/port composition |
|
|
fallback for |
|
|
optional, overrides the above |
|
|
fallback for |
|
|
optional, overrides the above |
|
|
required, min length enforced by MinIO/S3 itself |
|
|
|
|
|
|
|
|
optional |
|
|
required |
|
|
required |
|
|
fallback for |
|
|
fallback for |
|
|
fallback for |
|
|
fallback for |
|
|
optional, override the |
|
|
fallback for |
|
|
optional, overrides the above |
|
|
required |
|
|
required |
|
|
optional (defaults to a placeholder test address) |
|
|
optional |
|
|
optional, defaults to |
|
|
required only if |
|
|
optional, default disabled |
|
|
required only if e-INFRA login is enabled |
|
|
optional, extra directory of |
|
|
optional, explicit path to the instance dir containing |
initial_configuration.py / initial_rdm_config.py¶
Two additional modules ship plain module-level constants rather than
configure_*() functions:
oarepo_config.initial_configuration— currently justTHEME_FRONTPAGE = False.oarepo_config.initial_rdm_config— a large block of RDM/OAI-PMH defaults (OAI server fetchers/classes, dashboard/communities/requests routes, citation styles incl.iso690-author-date-cs, person/org and identifier schemes incl.scopusid/researcherid/vedidk/ico, IIIF preview settings, etc.) intended to replace pieces ofinvenio-app-rdm’s own defaults.
Full example¶
Adapted from a production invenio.cfg (CESNET catch-all data
repository), showing most of the functions together:
import oarepo_config as config
from invenio_i18n import lazy_gettext as _
config.initialize_i18n()
config.configure_generic_parameters(
languages=(("cs", _("Czech")),),
)
config.configure_ui(
code="datarepo",
name=_("CESNET Data Repository"),
description=_("Catch-all repository for Czech scientific data"),
)
config.configure_communities(
communities_roles=[
dict(name="owner", title=_("Community owner"), is_owner=True,
can_manage=True, can_curate=True,
can_manage_roles=["owner", "curator", "member", "submitter"]),
dict(name="curator", title=_("Curator"), can_manage=True,
can_curate=True, can_manage_roles=["member", "submitter"]),
dict(name="submitter", title=_("Submitter"), can_manage=True,
can_manage_roles=[]),
dict(name="member", title=_("Member")),
]
)
# Preferred, higher-level workflow helper - builds the same "WORKFLOWS"
# constant that the low-level register_workflow() would populate directly,
# from IndividualWorkflow()/CommunityWorkflow() definitions instead.
config.configure_workflows(
config.IndividualWorkflow(
publish_without_review=False,
review_required=True,
self_review_enabled=True,
draft_creation_roles=["submitter"],
publish_without_review_roles=["direct-publisher"],
),
config.CommunityWorkflow(
code="community",
label=_("Default Community Workflow"),
community_curator_roles=["curator", "owner"],
),
)
config.configure_cron()
config.configure_stats()
# custom model, registered separately from GLOBAL_SEARCH_MODELS
from datasets import datasets_model
datasets_model.register()
# e-INFRA OIDC login, toggled by INVENIO_REMOTE_AUTH_ENABLED
env = config.load_configuration_variables()
if env.get("INVENIO_REMOTE_AUTH_ENABLED", "no").lower() in ("true", "yes", "1"):
config.configure_einfra_oidc()
# AI-assisted record validation (oarepo-checks), optional - only configured
# if a token is available
if env.get("INVENIO_LLM_API_TOKEN"):
config.configure_llm(api_token=env["INVENIO_LLM_API_TOKEN"])
# plain overrides after all config.* calls
RDM_RECORDS_MAX_FILES_COUNT = 100
RDM_FILES_DEFAULT_QUOTA_SIZE = 1000 * (10**9)
How it works internally: functions that write to your module¶
Every configure_*() / initialize_*() / register_*() function ends by
calling an internal helper, set_constants_in_caller(), which walks two
stack frames up (past itself and past the configure_* function) and
assigns every UPPERCASE local() variable of that function directly into
the globals of the module that called it. In practice that module is
always invenio.cfg.
import oarepo_config as config
config.configure_ui(code="myrepo", name="My Repository")
# is roughly equivalent to writing, by hand, all of:
# APP_THEME = [...]
# THEME_SITENAME = ...
# REPOSITORY_NAME = ...
# ... etc.
# directly at module level in invenio.cfg
Three consequences of this design that matter when using the package:
Call these functions directly from
invenio.cfg(at module level, or from a function that is itself defined insideinvenio.cfg). Wrapping a call in a helper function that lives in a different importable module will make the constants land in the wrong module’s globals, because the frame lookup targets whichever module’s frame is two levels up.Functions that build on top of previously-set constants (
configure_oai()needsREPOSITORY_NAME,configure_ui()extendsAPP_DEFAULT_SECURE_HEADERS) read them back viaget_constant_from_caller(name, default)— so call order ininvenio.cfgmatters. See Ordering above.Dict/list/set-valued constants that already exist are extended rather than clobbered, via
merge_with_caller(name, value)— dicts are shallow merged (new keys win), sequences are concatenated, sets are unioned. This lets you pre-declare a partialCELERY_BEAT_SCHEDULEorOAUTHCLIENT_REMOTE_APPSbefore calling the relevantconfigure_*()and have it combined with the defaults instead of overwritten.