--- mpf: '0.7' title: Implementation — ESFI Docs url: https://docs.esfi.eu/standards/mpf/implementation/ mpf-url: https://docs.esfi.eu/standards/mpf/implementation/index.mpf organization: European Strategy & Future Infrastructure domain: docs.esfi.eu lang: en updated: '2026-11-03' author: ESFI provenance: human source-hash: 9f83b8bdbed2194796868d7becbbdb74ed2ca41eeb3112ddb8f92338cc35fe49 toc: - level: 1 text: Implementation id: implementation - level: 2 text: Requirements id: requirements - level: 2 text: 1\. Get the reference implementation id: 1-get-the-reference-implementation - level: 2 text: '2\. Declare the publisher: mpf.config.json' id: 2-declare-the-publisher-mpf-config-json - level: 3 text: Required keys id: required-keys - level: 3 text: Optional keys id: optional-keys - level: 2 text: 3\. Override per page id: 3-override-per-page - level: 2 text: 4\. Control what goes into the body id: 4-control-what-goes-into-the-body - level: 3 text: Content region id: content-region - level: 3 text: In-page markers id: in-page-markers - level: 2 text: 5\. Run the build last id: 5-run-the-build-last - level: 3 text: Command-line options id: command-line-options - level: 3 text: What the build writes id: what-the-build-writes - level: 3 text: Ordering and drift id: ordering-and-drift - level: 2 text: 6\. Sign (optional) id: 6-sign-optional - level: 2 text: 7\. Publish discovery id: 7-publish-discovery - level: 2 text: 8\. Verify id: 8-verify - level: 2 text: Troubleshooting id: troubleshooting - level: 2 text: On this page id: on-this-page spec-origin: esfi.eu content-origin: docs.esfi.eu content-id: esfi:page:standards-mpf-implementation content-parity: html: true human-visible-source: true hidden-sections: false summary: From an existing static site to published MPF, in one build step. index: true confidence: confirmed content-type: reference companion-footer: ESFI — civilization infrastructure · https://esfi.eu companion-contact: mailto:hermes@esfi.eu preferred-representation: mpf companion-scope: cite: true summarize: true train: true index: true relay: false language-variants: en: https://docs.esfi.eu/standards/mpf/implementation/index.mpf fr: https://docs.esfi.eu/fr/standards/mpf/implementation/index.mpf nl: https://docs.esfi.eu/nl/standards/mpf/implementation/index.mpf de: https://docs.esfi.eu/de/standards/mpf/implementation/index.mpf hr: https://docs.esfi.eu/hr/standards/mpf/implementation/index.mpf mpf-id: 87759419531da68ca0ae945ea6ccc62dd32382f985e2ddebf56c5838131b5422 --- Language ✕ /standards/mpf/implementation/index.mpf[open file ↗](/standards/mpf/implementation/index.mpf)[spec ↗](/standards/mpf/0.7)✕ loading… # Implementation {#implementation} **From an existing static site to published MPF, in one build step.** MPF is generated, not hand-written. The reference implementation, `mpf-build`, reads a site’s finished HTML and writes a `.mpf` companion beside every page, together with the site’s machine indexes. Nothing changes in production: the server keeps serving static files. This page is the implementation guide. Field definitions live in [Specification 0.7](/standards/mpf/0.7); discovery is covered in [Publishing and discovery](/standards/mpf/publishing-and-discovery). Every rule below is marked **Specification 0.7**, **Reference implementation** or **ESFI practice**; the markers are explained in the [Overview](/standards/mpf). ## Requirements {#requirements} - A site that produces static HTML files (any generator, or hand-written HTML). - Node.js 18 or later to run `mpf-build`. **Reference implementation** - Write access to the output directory, and to DNS if you publish the optional DNS declaration. ## 1\. Get the reference implementation {#1-get-the-reference-implementation} `mpf-build` is published as source at [git.esfi.eu/esfi/mpf](https://git.esfi.eu/esfi/mpf). It is not currently distributed through the npm registry. ``` git clone https://git.esfi.eu/esfi/mpf.git cd mpf/mpf-build npm install ``` Run it directly: ``` node ./bin/mpf-build.js --config ``` or make the `mpf-build` command available on your machine with `npm link` from the same directory. ## 2\. Declare the publisher: mpf.config.json {#2-declare-the-publisher-mpf-config-json} The configuration file states who the publisher is, which languages the site uses, and the default declarations every page carries. It usually lives in the project root. ``` { "mpf": "0.7", "organization": "Example Publisher", "domain": "example.org", "base-url": "https://example.org/", "spec-url": "https://docs.esfi.eu/spec/mpf/0.7", "author": "Example Publisher", "default-lang": "en", "languages": ["en"], "content-id-prefix": "example", "content-selector": "main", "description": "One line describing this publication.", "defaults": { "provenance": "human", "index": true, "companion-scope": { "cite": true, "summarize": true, "train": false, "index": true, "relay": false }, "companion-contact": "mailto:web@example.org", "companion-footer": "Example Publisher — https://example.org" } } ``` ### Required keys {#required-keys} The builder stops with an error if any of these is missing or invalid. **Reference implementation** | Key | Rule | | --- | --- | | `mpf` | Version string emitted in every file | | `organization`, `domain`, `author` | Non-empty strings | | `base-url` | Base URL of the site; trailing slashes are normalised | | `default-lang` | Must be one of `languages` | | `languages` | Non-empty array of language codes | | `content-id-prefix` | Namespace for generated `content-id` values; deliberately separate from `organization` so identifiers survive a change of name | | `defaults.provenance` | `human`, `ai-assisted` or `ai-generated` | | `defaults.index` | Boolean | ### Optional keys {#optional-keys} | Key | Effect | Status | | --- | --- | --- | | `spec-url` | Canonical specification URL, emitted as `MPF-spec` in `llms.txt` | **Specification 0.7** | | `description` | One-line description used in `llms.txt` and `llms.mpf` | **Specification 0.7** | | `content-selector` | CSS selector for the content region | **Specification 0.7** | | `spec-origin`, `content-origin` | Origin declarations in frontmatter; default to `domain` | **Specification 0.7** | | `signing-key` | Path to a private key; enables signatures (see step 6) | **Specification 0.7** (behaviour), **Reference implementation** (key handling) | | `defaults.confidence` | `confirmed`, `probable`, `speculative` or `contested` | **Reference implementation** (allowed values) | | `defaults.content-type` | `specification`, `narrative`, `data`, `analysis`, `reference`, `legal` or `profile` | **Reference implementation** (allowed values) | | `defaults.companion-scope` | Booleans for `cite`, `summarize`, `train`, `index`, `relay` | **Specification 0.7** (field), **Reference implementation** (validation) | | `defaults.content-parity` | Default parity declaration | **Specification 0.7** | | `defaults.companion-instruction` | Publisher message to the machine reader; also prepended to the body as a blockquote | **Specification 0.7** | | `defaults.companion-footer` | One-line publisher signature appended to the body; must not contain line breaks or HTML | **Specification 0.7** | | `defaults.companion-contact` | Contact for machine readers, e.g. `mailto:` | **Specification 0.7** | | `defaults.c2pa-manifest` | Pointer to a C2PA manifest | **Specification 0.7** | | `defaults.preferred-representation` | `mpf` or `html` | **Reference implementation** (allowed values) | Every declaration is a publisher decision. Treat the defaults as a statement your organisation will stand behind, not as technical settings. `companion-scope` records processing intent only. MPF does not carry licensing terms, pricing or compensation conditions; those belong to licensing mechanisms outside the format. ## 3\. Override per page {#3-override-per-page} Any page can override site defaults with a JSON sidecar beside its HTML file: `index.html` → `index.mpf.json`, `guide/page.html` → `guide/page.mpf.json`. **Specification 0.7** (existence), **Reference implementation** (allowed fields) ``` { "confidence": "speculative", "content-type": "analysis", "review-after": "2027-03-01", "companion-instruction": "This page contains projections. Treat figures as estimates." } ``` Allowed override fields: `title`, `author`, `summary`, `section`, `parent`, `lang`, `provenance`, `c2pa-manifest`, `confidence`, `content-type`, `valid-until`, `review-after`, `index`, `related`, `companion-instruction`, `companion-footer`, `companion-scope`, `companion-contact`, `preferred-representation`, `supersedes`, `superseded-by`, `language-variants`, `content-id`, `content-parity`, `signature`. An unknown field stops the build. `related` is an array of `{ "url", "relation" }`, where `relation` is one of `prerequisite`, `extends`, `source`, `contradicts`, `alternative`, `supersedes`, `details`. **Reference implementation** ## 4\. Control what goes into the body {#4-control-what-goes-into-the-body} ### Content region {#content-region} The builder takes the element matched by `content-selector`. If nothing matches, or the match is empty, it falls back in order to `main`, `article`, `[role="main"]`, `.content` and `body`. Inside the selected region it removes navigation, headers, footers, scripts, styles and elements marked `aria-hidden="true"`. A page with no usable content region is skipped and reported. **Specification 0.7** (content selection), **Reference implementation** (fallback order and noise list) ### In-page markers {#in-page-markers} Four HTML attributes let a publisher control the machine body without changing what people see. **Reference implementation** | Attribute | Effect | | --- | --- | | `data-mpf-only` | Content appears only in the MPF body, prepended to it. Its presence sets `content-parity` to `html: false`, `human-visible-source: false`, `hidden-sections: true`. | | `data-mpf-hide` | Content is excluded from the MPF body. | | `data-mpf-text="…"` | The element is replaced in the MPF body by the given text; useful for obfuscated contact addresses. | | `data-mpf-alt="…"` | Images, media and embeds are replaced by the given text. Embeds without it are dropped from the MPF body. | `data-mpf-only` is the one to use sparingly. Machine-only content is exactly what the parity declaration exists to make visible; the builder declares it automatically so it cannot be hidden. ## 5\. Run the build last {#5-run-the-build-last} Run `mpf-build` after the HTML is final, over the finished output directory: ``` "scripts": { "build": "astro build", "postbuild": "mpf-build dist --config mpf.config.json" } ``` Pass `--config` explicitly when processing a generated directory such as `dist/`, so the builder does not look for the configuration inside it. **Reference implementation** ### Command-line options {#command-line-options} **Specification 0.7** | Option | Effect | | --- | --- | | `--config ` | Configuration file; defaults to `/mpf.config.json` | | `--out ` | Write output to another directory; defaults to in place | | `--dry-run` | Report what would be written, write nothing | | `--verbose` | Log each processed page with title, URL and table-of-contents count | | `--watch` | Rebuild on changes to HTML, page overrides or configuration | ### What the build writes {#what-the-build-writes} | Artifact | Location | Status | | --- | --- | --- | | Page companion | Beside each page, `.html` → `.mpf` | **Specification 0.7** | | `llms.mpf` | Site root; MPF index of pages with `index: true` | **Specification 0.7** | | `llms.txt` | Site root; MPF section created or replaced, other content preserved | **Specification 0.7** | | `llms-full.txt` | Site root; bodies of indexed pages in the default language | **Specification 0.7** | | `.sig` files | Beside each `.mpf`, when signing is configured | **Specification 0.7** | Canonical URLs and `source-hash` values are always derived from the source HTML and its source-relative path, never from the output path. **Reference implementation** ### Ordering and drift {#ordering-and-drift} Each companion pins its source with `source-hash`. If the HTML is regenerated without re-running `mpf-build`, or the reverse, the pair drifts. A stale companion is worse than a missing one, because it looks authoritative. Keep MPF generation as the last step of the build and deploy both representations together. ## 6\. Sign (optional) {#6-sign-optional} When `signing-key` points to a private key file, the builder signs every page companion and `llms.mpf`, writes the signature base64-encoded to a `.sig` file beside it, and adds a `signature` URL to the frontmatter. If the key file is missing, the build continues without signatures and prints a warning. **Specification 0.7** (signature field and artifact), **Reference implementation** (encoding and key handling) The signature covers the complete MPF file as written. Publish the corresponding public key where readers can find it, and state which algorithm it uses. The key never belongs in the repository or the output directory. ## 7\. Publish discovery {#7-publish-discovery} The build produces `llms.txt` and `llms.mpf`. ESFI sites also publish a DNS TXT record, a `/.well-known/mpf` declaration and, optionally, a page-level alternate link. These are **ESFI practice**; how to set them up is described in [Publishing and discovery](/standards/mpf/publishing-and-discovery). ## 8\. Verify {#8-verify} Check what you published, not what you configured: ``` curl -s https://example.org/index.mpf | head -30 # companion exists and parses curl -s https://example.org/llms.mpf | head -30 # index exists and lists pages curl -s https://example.org/llms.txt # MPF section, MPF-spec, MPF-index curl -s https://example.org/.well-known/mpf # ESFI practice dig +short TXT _mpf.example.org # ESFI practice ``` Then check the relationship between the two representations: 1. **Drift.** Compute the SHA-256 of the served HTML and compare it with `source-hash`. A mismatch means the pair is out of step, unless the server alters HTML in transit. 2. **Identity.** `url` must be the canonical human page; `mpf-url` must be the address you fetched. 3. **Languages.** For each entry in `language-variants`, confirm the file exists. The builder generates these addresses from configuration and does not check them. 4. **Parity.** If `hidden-sections` is `true`, read the machine-only content and make sure you intend it. 5. **Scope.** Confirm that `companion-scope` in the files, `/.well-known/mpf` and the DNS record state the same values. Once your output can be inspected and matches a published version, it can be listed in the [Implementation registry](/standards/mpf/implementation-registry). ## Troubleshooting {#troubleshooting} | Symptom | Likely cause | Fix | | --- | --- | --- | | `skipped: … (no content root)` | Selector matches nothing and the fallbacks are empty | Set `content-selector` to the element that holds the page content | | Empty or near-empty bodies | Selector matches a wrapper without the content | Run with `--verbose` and adjust `content-selector` | | Build stops on configuration | Missing required key, or a value outside the allowed set | Read the error; it names the field | | Build stops on a page override | Unsupported field in a `.mpf.json` file | Remove the field or check its spelling (keys are kebab-case) | | `source-hash` mismatch after deploy | HTML changed after MPF generation | Re-run the full pipeline with MPF last | | Page missing from `llms.mpf` | `index` is `false` for that page | Check the page override and `defaults.index` | | `companion-footer` error | Footer contains a line break or HTML | Use a single plain-text line | | Wrong language on a page | Path does not start with a configured language code | Add the language to `languages` or set `lang` in the page override | * * * Specification: [Specification 0.7](/standards/mpf/0.7) · Discovery: [Publishing and discovery](/standards/mpf/publishing-and-discovery) · Source: [git.esfi.eu/esfi/mpf](https://git.esfi.eu/esfi/mpf) ## On this page {#on-this-page} ESFI — civilization infrastructure · https://esfi.eu