WP Manifestindependent plugin directory
manifest / developer / markdown-database-integration

Markdown Database Integration

WordPress database drop-in that stores content as markdown files. SQLite for machinery, markdown for knowledge. AI-native WordPress storage layer.

by Chris Huber · github.com/automattic/markdown-database-integration · website

5stars
42release downloads
1forks

Install

The author publishes release zips, so WP-CLI can install straight from GitHub:

wp plugin install https://github.com/automattic/markdown-database-integration/releases/download/v0.13.0/markdown-database-integration.zip

Readme

Markdown Database Integration

File-backed, reconstructable WordPress database state: Markdown for content, JSON for WordPress and plugin table rows, SQL for plugin schemas, and SQLite as a rebuildable query engine and index.

What This Does

MDI has two SQLite-backed operating modes:

  • mirror (the default) keeps the SQLite database authoritative and mirrors Markdown-backed post writes to files.
  • primary persists the state needed to reconstruct WordPress to ordinary files. SQLite remains the runtime query engine and index, but a cold boot can recreate it from the content and state trees.

SQLite Integration API

SQLite Database Integration is optional. It is required only by the sqlite backend; the default mdi-native backend serves WordPress directly from the canonical Markdown and JSON files and never loads it.

When the sqlite backend is selected, MDI requires a SQLite Database Integration release that includes the canonical PDO-compatible WP_MySQL_On_SQLite API, introduced by WordPress/sqlite-database-integration#449. MDI no longer uses the deprecated WP_SQLite_Driver compatibility layer. The runtime constructs the canonical driver with its mysql-on-sqlite: DSN and consumes query results as PDOStatement objects.

In primary mode, a typical single-root store looks like this:

wp-content/db/
  post/
    hello-markdown-world.md
    gutenberg-block-test.md
  page/
    about.md
    contact.md
  wiki/                         # Custom post type.
    woocommerce-pricing.md
  _options/
    siteurl.json
  _tables/
    users.json
    comments.json
    my_plugin_jobs.json
  _schema/
    my_plugin_jobs.sql

Post types get their own directories. Each Markdown-backed post file has YAML frontmatter for its row data, post meta, and terms, with stored post_content bytes as its body:

---
type: document
title: Gutenberg Block Test
description: A Gutenberg block editor smoke page.
resource: https://example.test/gutenberg-block-test
tags: [gutenberg, blocks]
timestamp: "2026-04-14T03:14:35+00:00"
wordpress:
  id: 7
  status: publish
  type: post
  author: 1
  date: "2026-04-14 03:14:35"
  modified: "2026-04-14 03:14:49"
  slug: gutenberg-block-test
---

## This is a heading block

Content goes here with **bold** and *italic* text.

- List item one
- List item two

> A blockquote for good measure.

MDI does not decide whether that body is Markdown, block markup, or HTML. It stores whatever the caller or content-format layer writes to post_content.

MDI manages the frontmatter shape automatically. Markdown files use portable, WordPress-compatible metadata: broadly useful concept fields stay at the top level, while WordPress round-trip fields live under wordpress. Existing MDI files are rewritten to the current shape by the one-time frontmatter migration during upgrade.

Primary-Mode Persistence

Primary mode persists the following state so SQLite can be reconstructed:

  • Post rows and content in post/*.md, page/*.md, and custom-post-type directories; their post meta and terms are in frontmatter.
  • Options as individual _options/*.json files.
  • Users and usermeta, taxonomy tables, comments and commentmeta, links, and non-Markdown posts as _tables/*.json snapshots.
  • Arbitrary plugin-table rows as _tables/*.json snapshots and their schemas as _schema/*.sql files.

The MARKDOWN_DB_TABLE_DURABILITY_POLICY wp-config.php array and markdown_db_table_durability_policy filter classify each table as canonical, reconstructible, or ephemeral. Canonical tables persist their schema and complete rows. Reconstructible tables may persist a bounded projection (query, limit, or partition settings), while their owner may recreate the runtime table when canonical state is absent. Ephemeral tables are excluded from mutation capture, schema and row snapshots, native registration, and cold hydration. Tables remain canonical by default.

The constant makes the policy available during drop-in cold reconstruction:

define( 'MARKDOWN_DB_TABLE_DURABILITY_POLICY', array(
    'runtime_events' => array( 'durability' => 'reconstructible', 'projection' => array( 'limit' => 100 ) ),
    'runtime_locks'  => 'ephemeral',
) );

The filter receives that normalized policy, unprefixed table name, and full table name, and may override it after hooks are available. For example:

add_filter( 'markdown_db_table_durability_policy', function ( $policy, $suffix ) {
    if ( 'runtime_events' === $suffix ) {
        return array( 'durability' => 'reconstructible', 'projection' => array( 'limit' => 100 ) );
    }
    if ( 'runtime_locks' === $suffix ) {
        return array( 'durability' => 'ephemeral' );
    }
    return $policy;
}, 10, 2 );

MARKDOWN_DB_EPHEMERAL_TABLES, markdown_db_ephemeral_tables, markdown_db_table_persistence_policy, markdown_db_persistent_table_query, and markdown_db_persistent_table_rows remain compatibility inputs to this resolver. Sites can migrate table classifications to the unified filter while retaining the query and row filters for existing projections.

Storage Boundary

MDI is a storage and persistence layer:

  • It persists database state to files and rebuilds or synchronizes the SQLite index from those files in primary mode.
  • It stores post_content bytes exactly as received.
  • It does not render markdown to HTML.
  • It does not convert editor block markup to markdown during normal writes.
  • It does not register render, REST, editor, or write-engine conversion hooks.

Content-format policy belongs to the application layer above MDI. A site can choose block markup, HTML, Markdown, or another format for post_content; MDI persists those bytes and database state without interpreting them.

Import/export is the explicit content-format boundary. The markdown-db import and markdown-db export commands and abilities use Block Format Bridge to round-trip between markdown files and serialized block content by default:

  • Import: markdownblocks
  • Export: blocksmarkdown
  • Raw byte preservation: pass --no-convert or set no_convert in the ability input.
  • Custom conversion: pass --from=<format> --to=<format>.
  • Policy override: filter markdown_db_content_format_conversion.
  • Layout selection: pass --layout-profile=<id> or configure MARKDOWN_DB_CONTENT_LAYOUT_PROFILE. External layouts register their complete enumerate, map_source, and path_for_post contract from the bootstrap file named by MARKDOWN_DB_CONTENT_LAYOUT_PROFILE_BOOTSTRAP, which is loaded before the db.php primary runtime starts.

Custom layout writes stage a temporary file in a verified destination directory. MDI rejects symlinked path segments and compares the directory device/inode before and after staging and immediately before rename. A detected directory replacement aborts and removes the staged file. This is PHP's strongest portable filesystem guarantee without an openat(2) descriptor API; deployments that permit an attacker to replace directories between the final identity check and rename() must protect the content root with filesystem ownership and permissions.

WRITE:

  WordPress caller writes post_content
       │
       ▼
  SQLite stores post_content bytes
       │
       ▼  MDI write engine
  .md file stores the same bytes


READ (site boots):

  .md file body
       │
       ▼  loaded AS-IS into in-memory SQLite
  post_content has the same bytes


IMPORT / EXPORT:

  .md file body or post_content
       │
       ▼  BFB conversion, unless disabled
  target file body or post_content


RENDER / EDITOR / API:

  Handled by the application/content-format layer, not MDI.

Dependencies

MDI requires Block Format Bridge for self-contained import/export conversion. The drop-in and live write engine remain byte-preserving; BFB is not used by the runtime render, REST, editor, or DB write paths.

Why

File-backed primary state makes a WordPress site reconstructable instead of depending solely on one SQLite file. That supports:

  • Portability between machines or fresh WordPress installations.
  • Git review, history, and replication for content and any state roots a site chooses to version.
  • Direct inspection and editing of content by people, local tools, and AI agents.
  • Backups and recovery by rebuilding a disposable SQLite runtime from the persisted files.
  • Disposable local or test runtimes that can be recreated from the same trees.

Architecture

WordPress Core ($wpdb)
        │
        v
WP_Markdown_Driver -------------------- runtime queries ----> SQLite index
        │                                                        ^
        │ successful writes                                      │ rebuild/sync
        v                                                        │
Persisted files ---------------------- cold/warm boot --> MDI loader
        │
        ├── MARKDOWN_DB_CONTENT_DIR
        │     post/*.md, page/*.md, {type}/*.md
        │
        └── MARKDOWN_DB_STATE_DIR
              _options/*.json, _tables/*.json, _schema/*.sql,
              markdown-index.sqlite

Durable Reconciliation Operations

Bounded reconciliation can use WP_Markdown_Durable_Reconciliation_Operations to coordinate mutations that cross the WordPress and canonical-filesystem durability domains. Operations bind a plan and continuation, canonical root, normalized resource identity, direction and kind, and exact normalized before and after identities. The lifecycle is explicit:

planned -> claimed -> effect_observed -> completed
                  \-> ambiguous -> reconciliation_required

WP_Markdown_Reconciliation_Adapter keeps WordPress, SQLite, MySQL, and file observation/mutation details behind the owning adapter. Recovery of an ordinary operation is observation-only and completes only when every named domain exactly proves the intended after identity. A separately prepared WordPress commit may continue its canonical effect only after its exact commit checkpoint is proven. Missing, indeterminate, or divergent evidence is persisted as a structured reconciliation_required conflict.

Because only an owning backend can make resource fencing atomic with its effect, an adapter installs each claimed fence and must atomically reject any token that is no longer current for the normalized resource. The generic store also rejects expired-lease transitions; together these prevent a stale worker from publishing progress or mutating after a replacement owner has fenced it.

The supplied filesystem operation store uses revision/state/fence compare-and-set transitions, expiring ownership leases, and monotonically increasing fencing tokens. Its HMAC-authenticated, atomically replaced journal has caller-configured record and byte bounds, retains terminal identities to prevent stale intent from being planned again, and rejects placement within or above a managed canonical root. Operations are accepted only for roots authorized when the store is constructed, and at least one root must be declared. Deployments should derive its authentication key from server-only secret material and place it in a server-owned runtime directory. This journal is runtime coordination state, not canonical content; it has no Git or publication requirement.

Read the full README on GitHub →

Releases

TagPublishedAssetDownloads
v0.13.0 Sep 2, 2026 markdown-database-integration.zip 0
v0.12.0 Aug 30, 2026 markdown-database-integration.zip 0
v0.11.5 Aug 25, 2026 markdown-database-integration.zip 2
v0.11.4 Aug 25, 2026 markdown-database-integration.zip 0
v0.11.3 Aug 25, 2026 markdown-database-integration.zip 0
v0.11.2 Aug 25, 2026 markdown-database-integration.zip 0
v0.11.1 Aug 25, 2026 markdown-database-integration.zip 0
v0.11.0 Aug 25, 2026 markdown-database-integration.zip 1
v0.10.2 Aug 23, 2026 markdown-database-integration.zip 3
v0.10.1 Aug 23, 2026 markdown-database-integration.zip 2
v0.10.0 Aug 23, 2026 markdown-database-integration.zip 3
v0.9.2 Aug 12, 2026 markdown-database-integration.zip 1
v0.9.1 Aug 12, 2026 markdown-database-integration.zip 1
v0.9.0 Aug 12, 2026 markdown-database-integration.zip 2
v0.8.9 Jul 29, 2026 markdown-database-integration.zip 1
v0.8.8 Jul 29, 2026 markdown-database-integration.zip 0
v0.8.7 Jul 29, 2026 markdown-database-integration.zip 0
v0.8.6 Jul 29, 2026 markdown-database-integration.zip 1
v0.8.5 Jul 29, 2026 markdown-database-integration.zip 4
v0.8.4 Jul 28, 2026 markdown-database-integration.zip 3
v0.8.3 Jun 25, 2026 01-markdown-database-integration.zip 5
v0.8.3 Jun 25, 2026 markdown-database-integration.zip 3
v0.8.2 Jun 17, 2026 markdown-database-integration.zip 3
v0.8.1 Jun 16, 2026 markdown-database-integration.zip 1
v0.8.0 May 27, 2026 markdown-database-integration.zip 4