Skip to content

Configuration reference

manni graph reads one file, manni.config.yaml, and one key in it, graph:. Two top-level keys beside it belong to the family rather than to this tool. collections: says which documents every tool reads. providers: says how every tool reaches a model.

Discovery walks from the working directory up to the repository root. -c names a file instead, --no-config ignores whatever discovery found, and with no file at all every value below is its built-in default.

Inside graph: unknown keys are refused, so a typo is caught rather than silently ignored:

Terminal window
$ manni graph build
manni: Invalid config in manni.config.yaml:
/graph/fill: unknown key "maxCostUsd"
# The family's document sets. Not a graph key: every tool reads these.
collections:
- name: site
paths:
- "docs/**/*.{md,mdx}"
exclude:
- "docs/drafts/**"
# The family's inference settings. Not a graph key either.
providers:
provider: anthropic
graph:
baseIri: https://example.com/graph/
out: graph/graph.ttl
authoring: true
routes:
- basePath: /
root: docs
extensions: [".md", ".mdx", ".markdown", ".html", ".htm", ".adoc", ".asciidoc", ".rst", ".dita", ".ditamap", ".flsnp", ".fltoc", ".flbrs"]
indexFiles: ["index", "README"]
language: en
build:
derive: [frontmatter, sections, links, tags, images, code, provenance, terms]
check:
shapes: []
questions:
top: 5
severity: warning
provenance:
qualified: true
stats:
coverageThreshold: 80
fill:
temperature: 0
maxTurns: null
cacheDir: .manni/graph/cache
confidenceThreshold: 0.7
writeProvenance: true
validateGraph: true
sections: false
fields: [label, alt-labels, broader, narrower, related-concepts, concepts,
type, applies-to, about-product-lifecycle, about-product-aspect,
not-applicable-to, not-about-product-aspect, sample-questions]
chunkChars: 12000
offline: false
questions:
perPage: 3
embed:
model: onnx-community/granite-embedding-small-english-r2-ONNX
dtype: q8
out: graph
cacheDir: .manni/graph/embed-cache
byLanguage:
de:
model: Xenova/bge-small-en-v1.5
export:
iirds:
title: Product documentation
creator: Example Corp
version: "1.3"
chunks:
evals: .manni/evals.json
llms:
title: Product documentation
summary: Everything the product does, page by page.
origin: https://docs.example.com
retrieve:
seeds: 3
depth: 1
maxChars: 12000
withhold: []
serve:
maxChars: 40000

Section keys are camelCase (baseIri, maxTurns, confidenceThreshold). The page vocabulary a document writes is kebab-case (alt-labels, applies-to). The two are different surfaces and spell things differently on purpose.

collections: is the family’s, declared once and read by every tool. See the metadata tool’s configuration reference for its full shape.

FieldTypeDefaultWhat it does
namestringrequiredWhat --collection <name> selects.
pathslist of globsrequiredMembers, resolved from the config file’s directory rather than from where the command runs.
excludelist of globs[]Non-members. It decides membership, so an excluded file is not in the collection even when a positional path names it.
urlstringnoneWhere those pages are published. manni a11y check reads it; graph does not.
externalMetadatalist[]Frontmatter kept outside the document. build reads each page merged with it, as manni meta validate does, so an external graph block derives exactly as an inline one does. fill writes a key a manifest owns to that manifest.

A build or fill run with no positional paths reads every declared collection. --collection narrows that to one, and cannot be combined with paths.

Nothing matching is exit 2:

Terminal window
$ manni graph build
manni: No input files matched: docs/**/*.rst (cwd: /repo). Fix the globs, or pass --allow-empty if matching nothing is expected.

providers: is the family’s too. manni graph fill is the one verb that reaches a model, and it resolves one in this order, later winning:

  1. providers.provider, the family default, with the connection settings for each provider beside it.
  2. graph.provider, this tool’s override.
  3. --provider, this run’s.
  4. --local, over all three, running llama-cpp on this machine.

Unset everywhere, the provider is auto: an Anthropic key, then an OpenAI key, then the Claude CLI, then a local model, detected exactly as manni meta fill detects. A model is carried only to the provider its own level names, because a model name does not say which provider owns it. Under auto a model is a usage error.

KeyTypeDefaultWhat it does
providerstringunsetWhich provider graph fill sends pages to: auto, anthropic, openai, claude-cli, llama-cpp. An unknown name is refused when the config is read, by every verb, not only the one that reaches a model.
modelstringunsetThe model within the provider named beside it. Unset, the inference library’s default for that provider is used; manni pins none, and every cache key names the model it resolved to. This is not embed.model.
baseIristringurn:manni:graph:The base every minted node IRI hangs off. A value not ending in / or : gets a trailing slash. Use an HTTP base when the graph is published, so its nodes dereference.
outstringgraph/graph.ttlWhere graph build writes the Turtle, and where every graph reader looks for it. Resolved from the working directory. -o overrides it for one build, -g for one read.
authoringbooleantrueWhether a build carries author-facing content: the strategy records, typed, and every value a page’s schemas mark x-manni-location: external, at any depth. false leaves both out of every build, export and publish. --no-authoring sets it false for one run. See a graph for readers.
questionFileslist of paths[]Golden-set files, relative to the config file. They hold questions that span pages or that the docs don’t answer. graph check asks them beside each page’s sample-questions. A missing file, or one that fails its schema, stops check with exit 2.

Maps published site paths back to source files, so a link written as the reader sees it resolves to the page that serves it. Each entry is an object; only root is required.

KeyTypeDefaultWhat it does
basePathstring/The site prefix this route serves. Normalized to a leading / and no trailing one; / means the site root.
rootstringrequiredThe directory that prefix resolves into, relative to the config file.
extensionslist of strings[".md", ".mdx", ".markdown", ".html", ".htm", ".adoc", ".asciidoc", ".rst", ".dita", ".ditamap", ".flsnp", ".fltoc", ".flbrs"]Extensions to try, in order, when a route names a page without one. Markdown comes first, so a tree holding install.md and a built install.html resolves to the source. Each must start with a dot.
indexFileslist of strings["index", "README"]Basenames to try for a directory route such as /guides/.
languageBCP-47 tagunsetThe language of every document under root. A page’s own lang or language frontmatter wins; absent that, the nearest enclosing route that declares one applies. A MadCap Flare topic under no such route takes its project’s xml:lang. Unset, the route says nothing about language.

Routes are corpus-defining, so they are config-only. There is no flag.

KeyTypeDefaultWhat it does
derivelistall eightWhich sources become triples: frontmatter, sections, links, tags, images, code, provenance, terms. Narrowing it makes a smaller graph, not a wrong one; a predicate nothing derives simply has no triples. terms reads the termbase manni term check reads, from the same config, and makes each term a concept. Every concept label then resolves through it first. A list that leaves terms out mints every concept from its label.
KeyTypeDefaultWhat it does
shapeslist of paths[]The SHACL shapes graph check validates the built graph against. Empty means the shapes shipped in the package, shapes/graph/shapes-1.4.0.ttl. Naming shapes here replaces the bundled set rather than adding to it, so list the bundled file too when you mean to extend it. --shapes overrides this for one run.
questions.topinteger, 1 or more5How high a page must rank for each of its own sample-questions, or the section holding a pinned answer. graph check asks each question through the retrieval graph search uses, and reports one outside the top results.
questions.severitynotice, warning, error or offwarningThe severity of a question that does not find its page or its answer, and of an answer whose lines changed. A question asked twice, and an answer whose page is gone, take it too. A drafted question or pin reports at notice instead. error fails the run. off reports none of these, and still reports a moved answer.
questions.foundThresholdnumber 0–100, or nullnullgraph check exits 1 when the percentage of reviewed questions found falls below this. It is the same scale as stats.coverageThreshold. questions.severity does not touch it. null sets no rate gate.
questions.citedThresholdnumber 0–100, or nullnullgraph check exits 1 when the percentage of reviewed questions graph retrieve cites falls below this. It applies only when a chunk artifact sits beside the graph. null sets no gate. See Cited by graph retrieve.
graph:
questionFiles: [docs/questions.yaml]
check:
questions:
top: 5
severity: warning
foundThreshold: 95

The found rate counts reviewed questions that have a test: a page, or at least one pin. Gaps and drafts are left out. A question with several answers is found only when every answer is. A changed answer, or one whose page is gone, counts as not found.

The questions have no flag. They are asked in their page’s language, against an index built from the graph in memory. A vector sidecar and an embedder beside the graph add the vector leg, as they do for graph search.

KeyTypeDefaultWhat it does
qualifiedbooleantrueEmit qualified PROV attribution and association nodes, which carry a role, rather than the bare prov:wasAttributedTo edge alone.

Git itself is detected, never declared. History is used wherever git can run over a repository. A run that finds neither git nor a repository warns once with git’s own reason and builds the rest. There is no switch, because a switch for a detectable fact is one more way to be wrong. A job that needs the stronger contract writes it as a shape over the built graph.

KeyTypeDefaultWhat it does
coverageThresholdnumber 0–100, or a map{}The minimum share of documents that must carry each measured field. A single number applies to every measured field but sample-questions, which only a map gates. A map sets them one at a time, keyed by field name. Empty gates nothing. Enforced only under stats --check; --coverage-threshold overrides it for one run.

There are thirteen measured document-level fields, and the Coverage page lists them with what fills each.

graph:
stats:
coverageThreshold:
title: 100
description: 90
type: 60
KeyTypeDefaultWhat it does
temperaturenumber 0–20Sampling temperature for the proposal call. Zero is the default because a reproducible proposal is worth more than a varied one.
maxTurnsinteger ≥ 1, or nullnullStop after this many inference calls. One page is one turn; a cached page spends none. null is unbounded. A page the budget stopped at is reported skipped, reason turn budget.
cacheDirstring.manni/graph/cacheWhere raw proposals are cached. Caching happens before the confidence gate, so re-running at a different --confidence spends nothing.
confidenceThresholdnumber 0–10.7The self-reported confidence a proposed value must clear to be written. Below it the value is reported and not written.
writeProvenancebooleantrueRecord what was written in the page’s meta-provenance, naming the model and the fields. A drafted question gets generated-by and confidence instead. Turning it off removes the review trail; leave it on.
validateGraphbooleantrueVet each proposal against the SHACL shapes before writing it, simulating it in the derived graph. Accepted proposals fold into that state, so two pages in one run cannot jointly corrupt the graph.
sectionsbooleanfalseAlso propose per-section metadata, not only page-level. Opt-in, because it is a great deal more output to review.
fieldslistevery fieldWhich fields may be proposed at all: label, alt-labels, broader, narrower, related-concepts, concepts, type, applies-to, about-product-lifecycle, about-product-aspect, not-applicable-to, not-about-product-aspect, sample-questions.
chunkCharsinteger ≥ 112000The most characters of a page one question request sends. A longer body is split at line boundaries. The name, default and bounds match meta fill’s. --chunk-chars overrides it.
offlinebooleanfalseSend nothing off this machine, and run only a local model already downloaded. It means what offline means for meta fill and term fill. --offline overrides it.
questions.perPageinteger ≥ 13fill drafts questions until a page has this many, reviewed or drafted.

graph publish runs its embed step only when this section exists, even empty. It runs the iiRDS export only when export.iirds exists.

KeyTypeDefaultWhat it does
modelstringonnx-community/granite-embedding-small-english-r2-ONNXThe local embedding model. Any id is accepted: the documented table is the tested set, not the permitted one, so a newer model works without a manni release.
dtypestringq8Weight quantization. q8 keeps int32 accumulation, which is associative, and so keeps the sidecars reproducible.
outstringthe index directoryDirectory for the vector sidecars, one file per language. Resolved from the working directory. -o overrides it for one run.
cacheDirstring.manni/graph/embed-cachePer-text vector cache, keyed on text, model and dtype.
byLanguagemap, BCP-47 tag → {model, dtype}{}Per-language overrides. A German corpus embedded with an English-only model returns confident, meaningless vectors and fails nothing, which is why this key exists. Unlisted languages use the two settings above.

Tested models, with what each costs:

ModelNote
onnx-community/granite-embedding-small-english-r2-ONNXDefault. 384 dimensions, an 8192-token context, so a section is never truncated. No prefixes.
Xenova/gte-smallLighter, about 34 MB. 384 dimensions, 512-token context.
Xenova/bge-small-en-v1.5384 dimensions. Needs a query prefix, applied automatically here, because omitting it degrades retrieval silently.
Xenova/all-MiniLM-L6-v2Smallest, about 23 MB, but truncates at 256 wordpieces, so a long section loses its tail.
KeyTypeDefaultWhat it does
iirds.titlestringthe exporter’s ownPackage title written into the iiRDS container.
iirds.creatorstringunsetCreator organization, written as an iiRDS Party and a vCard organization.
iirds.version"1.2" or "1.3""1.3"The iiRDS version literal the package declares. Quote it: unquoted, YAML reads 1.3 as a number and the value is refused.
chunks.evalspath, or nullnullA docevals JSON report, as manni docevals run -f json writes it, relative to the config file. It fills each chunk record’s evals. null leaves every record’s evals null. --evals overrides it for one run. Eval verdicts are author-facing, so a run with authoring off does not read it, and warns once when it is set.
llms.titlestringnoneThe H1 of llms.txt. graph export llms needs it. graph publish writes llms.txt only when the llms section exists.
llms.summarystringnoneThe blockquote under the H1.
llms.originURLnoneThe site’s scheme and host, such as https://docs.example.com. Each link is this plus the page’s route. graph export llms needs it.

The retrieval settings. graph lookup, graph retrieve and graph traverse --context read them, and a flag on retrieve overrides each one for a run. graph export chunks records the resolved values under the manifest’s retrieve, the defaults a script’s createRetriever reads. None of these keys is required.

KeyTypeDefaultWhat it does
seedsinteger ≥ 13The number of top-ranked passages retrieval starts from. lookup returns this many matches. --seeds overrides it.
depthinteger ≥ 01The number of graph hops retrieval follows out from those passages. lookup’s outward walk uses it. --depth overrides it.
maxCharsinteger ≥ 112000The most characters of passage text a bundle returns. On lookup it applies to each section on its own, and it only ever drops text, never a node or an edge. --max-chars overrides it.
withholdlist of drifted, failing[]Applied at export. A matching passage is left out of the rows and listed under the manifest’s withheld. drifted matches a pin at error severity. failing matches a current eval verdict that failed or errored, and a stale verdict never counts.
graph:
export:
chunks:
evals: .manni/evals.json
retrieve:
withhold: [drifted, failing]

See Chunks for the files the export writes.

What graph serve sends an agent over MCP. The key is config-only, and it is not required. The CLI twins print the full result whatever it says.

KeyTypeDefaultWhat it does
maxCharsinteger ≥ 140000The most characters one served answer may take, as JSON. Past it, passages go out as references with no text, then edges go. What an answer carries gives the order.

Refused by name, with the replacement in the message, rather than aliased. An alias would be a second place to declare one thing.

KeyWhat to write instead
graph.inputsA top-level collections: entry. Document sets are declared once for every tool.
graph.excludeThe collection’s own exclude:, or --exclude for one run.
graph.versionNothing. The tool’s version is the package’s.
graph.provenance.gitNothing. Git is detected; a job that requires history expresses it as a shape.
graph.fill.providergraph.provider, or the family’s providers.provider.
graph.fill.maxCostUsdgraph.fill.maxTurns. The budget is counted in inference calls, which every model makes and every model can be counted making. Converting turns to money is your provider’s rate card.
kg.provenance on a pageThe page-level meta-provenance, with JSON Pointers. The page block is graph: now. Unlike the keys above, a leftover kg: block is not refused. It validates and derives nothing.

Which fields reach an exported artifact is the schema’s call, not this tool’s. Mark a top-level property x-manni-graph-output: false in the schema that governs the page. It is then dropped before derivation, so it appears in none of the four outputs. Absent means true. That holds whatever authoring says. The mark sits beside x-manni-location in the same schema, so what a page stores and what a delivered artifact says are two decisions in one place. graph export chunks reads both marks at every depth, as Chunks describes.

graph keeps no schema set of its own. graph build reads the marks from the set manni meta validate resolves for each page, so schemas, strict, register and overrides under meta: decide them. The default set carries manni:stewardship:1.2.0, which marks owner, stakeholders and reviewed-by, so a published graph leaves those three out with no config line. See Govern the graph and how a schema reaches a file.