Mova0.6 pre-release⌕
☰
home / docs / repo-docs / Add-on modules

Add-on modules

Mova can load add-on modules: data packages that are not part of Mova itself, each under its own licence. The first kind is the dictionary layer (for example a dictionary built from Wiktionary, which is CC BY-SA). A module is installed into the data directory; it is never in the repository, a release's core files or a binary. The core and its built-in dictionaries (the pupils' dictionary and the commands dictionary, CC BY-SA 4.0 data) contain no module data.

Code: crate mova-modules (tools/modules; the mova binary) and the dictionary store world::dictstore.

What a module is

A folder <root>/<name>/<version>/ holding module.json and the files it lists:

{"schema": 1, "name": "example-dict", "version": "2026.11.04", "kind": "dictionary-layer",
 "licence": "CC-BY-SA-4.0", "licence_class": "cc-by-sa",
 "attribution": {"text": "From Example, CC BY-SA 4.0", "sources": ["https://example.org/"]},
 "files": [{"path": "layers/senses.tsv", "sha256": "…64 hex…", "bytes": 1234}],
 "provides": [{"kind": "dictionary-layer", "layer": "senses", "path": "layers/senses.tsv"}],
 "requires_mova": ">=0.5"}
fieldmeaning
schema1
name, version[A-Za-z0-9._-], not starting with a dot; the folder must be <root>/<name>/<version>
kindwhat the module is, e.g. dictionary-layer
licenceSPDX-like text, e.g. CC-BY-SA-4.0
licence_classours, cc-by-sa, cc-by, public-domain or nc-research (below)
attributiona text, or {"text", "sources": [URL…]}
filesevery file except module.json: relative path ([A-Za-z0-9._-] segments, no ..), SHA-256, size
provideswhat Mova can load: {"kind": "dictionary-layer", "layer": NAME, "path": REL}; optional "state" (draft, the default, admitted or derived) and, for an admitted layer, "review" (a listed file holding its review record)
requires_movae.g. >=0.5 or >=0.5, <1; compared with the module mechanism's release (mova_modules::MOVA_RELEASE, 0.5)

Unknown fields are ignored, so the format grows compatibly. A dictionary layer file has the columns of the school layers (word, part of speech, meaning for a pupil, sense in the lessons, examples, note; # lines are comments).

Where modules live

Roots, searched in order: each folder of MOVA_MODULES_PATH (:-separated), then modules (MOVA_DATA defaults to ~/mova-data). The same name and version in two roots: the first root's copy is used and the other is listed as shadowed. A root inside a Git work tree is refused.

The command

mova modules list                                   # installed modules, licences, layers, folders
mova modules info NAME[@VERSION]                    # licence notice, attribution, what it provides
mova modules verify NAME[@VERSION]                  # every file's size and SHA-256 against module.json
mova modules install https://…/module.json          # fetch the manifest and the files next to it, check, install
mova modules install DIR                            # or from a module folder on disk
mova modules install-torrent FILE.torrent|MAGNET    # download with aria2c, then install
mova modules make-torrent DIR OUT.torrent [--tracker URL]… [--web-seed URL]…
mova modules remove NAME@VERSION

world modules … runs the same commands. Options: --dir ROOT installs into ROOT (and is searched first), --sha256 HASH pins the module.json bytes, --accept-nc is needed for a non-commercial / research-only module.

Publishing a module: put the module folder (module.json and its files, same relative paths) under an HTTPS folder, give users the module.json URL (and its SHA-256 for --sha256), and optionally a .torrent made with make-torrent --web-seed URL/. A client fetches a multi-file web seed as <URL/><name>-<version>/<path> (BEP 19), so the web seed is the folder that holds a <name>-<version>/ copy (or link) of the module folder; make-torrent refuses a web seed that is not an https:// URL ending in /.

A release's files: mova fetch

The same binary downloads the files that come with a release beside the code (the trained model, the corpus, the dictionaries). Add-on modules such as wiktionary-en are separate, each with its own manifest when it is published; release 0.5 ships none. What they are, their sizes, SHA-256, licences, HTTPS URLs, .torrent files and magnet links are in the release's manifest in the repository: Mova 0.5 — the release files and the same data for programs, releases/0.5-files.json, which the 0.5 build of mova carries inside. A test (fetch::tests::the_shipped_manifest_follows_the_policy_and_the_bone_agrees) checks that the Markdown page is the rendering of the JSON and that every row follows the data policy (the model and the corpus CC BY-SA 4.0, the dictionaries CC BY-SA 4.0 with citations, Wikipedia or Wiktionary data only as a separate module, only the model required).

mova fetch 0.5                           # the required files (the model) over HTTPS; install.sh runs this
mova fetch 0.5 all --via torrent         # every file, from its .torrent
mova fetch 0.5 corpus --via magnet       # from the magnet link
mova fetch 0.5 corpus --archive corpus-0.5.tar.gz   # a file a BitTorrent client downloaded

Options: --manifest FILE (another release's manifest), --base URL (a mirror holding the same files), --keep-archive (keep the .tar.gz under <release>, e.g. to seed it).

Each way ends in the same check: the archive's size and SHA-256 must equal the manifest's, or it is refused and nothing is unpacked. torrent first checks that the .torrent's info hash is the manifest's and, after the download from the torrent's web seed, every piece's SHA-1; magnet takes the .torrent from the link's xs source and checks that it hashes to the link's info hash. Then the archive (one top folder) is unpacked under <unpack_to> (the release's folder release/0.5/, or the module root for a module, which is then verified against its module.json), and each install entry is copied into place (the model: en/models/en.bin, en/models/gparse/, coref/gender.tsv). An earlier different file is kept beside it as <name>.before-0.5.

What of BitTorrent Mova implements: bencode, single-file v1 metainfo, the info hash, magnet links (xt btih in hex or base32, dn, xl, tr, ws, xs), piece verification and BEP 19 web seeds over HTTPS. It does not implement the peer wire protocol, trackers, the DHT or metadata exchange (BEP 9): to download from peers, use a BitTorrent client with the .torrent or the magnet link and give its file to --archive. Our seeder (a client that implements all of those) serves every release file; each torrent also has the HTTPS file as its web seed, so a client completes without peers.

Loading dictionary layers

The dictionary store (world::dictstore, building the dictionary) loads module layers in two ways:

On every load the layer file is checked against the module's SHA-256. Each entry keeps its provenance through its layer: module, version, licence, class and attribution (world dict lookup prints them, world dict stats prints the attribution). As with every layer, a draft is loaded and shown but is knowledge only with allow_unreviewed. The store's fingerprint names each module and version, so a retained state learned with a module is examined only with the same module.

A layer may carry columns after the six of the school layers. The store keeps them per entry as one text (Entry::extra) and reads them by name (Entry::col, Entry::terms; dictstore::EXTRA_COLUMNS: labels, synonyms, antonyms, forms, form of, gaps, provenance). The school layers have none, so every reader of the six columns sees the same entries.

Modules Mova makes

modulemade bylicencelayer
wiktionary-entools/wikt-extract build from the raw English Wiktionary dump (Data, libraries and licences, «Wiktionary»)CC-BY-SA-4.0 AND GFDL-1.3-or-later, class cc-by-sa; attribution to the Wiktionary contributors with the dump datewiktionary-en (draft): the English entries, one row per part of speech, with the extra columns
wikt-extract build $MOVA_DATA/raw/wiktionary/enwiktionary/20260901 $MOVA_DATA/modules/wiktionary-en/20260901-1
mova modules verify wiktionary-en
MOVA_MODULES=wiktionary-en world dict lookup telly

Licence classes and gates

classloadswhat Mova derives from it
ours, public-domainalwaysours
cc-byalways; the attribution is shownours, crediting the source when shared
cc-by-saalways (the user's run; the module is not shipped by Mova)marked with the module's licence; never in the repository or shipped files
nc-researchonly for internal runs and measurements (MOVA_DICT_USE=internal); install needs --accept-ncmarked; never shipped

The gates, each with a red control in the tests:

Tests

cargo test --release -p mova-modules (manifest rules, versions, install/verify/remove, pinned hashes, HTTP refused, the release manifest and its page, mova fetch by HTTPS, torrent and magnet with a changed byte refused by each, NC acceptance, Git work tree refused, bencode, a deterministic torrent whose pieces and info hash are recomputed) and cargo test --release -p world --lib dictstore (module_layers_keep_provenance_and_licence_gates, derived_output_of_a_share_alike_module_never_ships). Red controls run on 2026-11-04: with guard_derived_write disabled, with the non-commercial check of shippable removed, and with the research-only purpose check removed, the dictstore tests fail; restored, they pass.

Limits

This page in Markdown → https://md.mova.elamur.ai/free/en/docs/repo-docs/modules/