# MojoMojo — deep dive (findings) **Date:** 2026-06-14 · **Source:** SHARD-WP-0003 T8 · **Subject:** MojoMojo, a Perl Catalyst wiki/CMS. ## Why this dive The file-store classics (TWiki, Foswiki, Oddmuse, UseMod) keep pages as files; the modern SaaS keep them behind APIs. MojoMojo is the **classic relational-DB-backed** wiki — a Catalyst MVC app over **DBIx::Class** with pages and their history in **SQL tables**, and **no file store and no first-class content API**. It anchors the *"attach by reading the database directly"* hard case the adapter contract must account for (T13/T14). ## 1. Architecture - **Stack:** Perl **Catalyst** (MVC web framework) + **DBIx::Class** (ORM) over a relational DB (SQLite / PostgreSQL / MySQL). Templating via Template Toolkit. - **Content:** **Markdown** (Text::MultiMarkdown) is the page markup — so the *body* is Markdown, but it lives **in a DB column**, not a file. - **Pages are hierarchical:** a **path tree** (`/parent/child`) modeled as rows with parent/lineage relations — structure is relational, not directory-based. - **Versioning:** each page edit creates a **new version row** (a `page_version`-style table) — full revision history lives in **DB version tables**, with author/timestamp. - **Features:** inline **AJAX editing**, **attachments** (stored as DB rows / blobs + metadata), diffs, RSS feeds, full-text search, per-page permissions. ## 2. The attach problem — DB or nothing MojoMojo exposes its content through the **web app** (HTML) and the **database**; there is **no clean REST/GraphQL content API** and **no file store**. So a shard adapter has two realistic paths: 1. **Direct relational read** (preferred): read the `page` + `page_version` (+ `content`, `attachment`) tables via DBIx::Class schema — pages, the path tree, and **full history** are all there, importable to the coordination journal (UC-41-style history import, but from **DB version rows** rather than RCS/git). 2. **HTML scrape** (fallback): parse rendered pages — lossy, last resort. This makes MojoMojo the **direct-DB-read** binding archetype: the canonical store is a relational schema, and the adapter's job is to map that schema to the wiki page model + journal. ## 3. Capability profile | Dimension (synthesis spectrum) | MojoMojo | |--------------------------------|----------| | Attachment mode | **direct DB read** (relational); HTML-scrape fallback; no file store, no API | | Addressing granularity | page (row); path tree via lineage rows | | Content identity | DB page id; path as human key | | Identity vs placement | row id vs path lineage (separable) | | Structure | **relational**: page rows + parent/lineage; attachments as rows | | History | **DB version tables** (per-edit version rows, author/timestamp) | | Merge model | app-level last-writer; DB transactions | | Native query | SQL over the schema (not a wiki query language) | | Translation | **Markdown body in a DB column** — minimal translation, but extraction needed | | Write granularity | page (row) per save | | Operational envelope | a Perl app + its DB; direct DB access needs credentials | | Access grant | per-page permissions in DB; app auth | | Content opacity | transparent if you can read the DB | | Provenance | author/timestamp on version rows | ## 4. INTENT mapping ### Reinforcements - **Backend-neutral page model**: the body is **Markdown** — once extracted from the DB column it maps directly; the adapter's work is **schema→page-model**, not format translation. - **History portability** (T13): DB **version rows** are a third history-source shape beside git commits and RCS files — importable to the journal as discrete revisions with author/timestamp. - **Graceful degradation**: even with only DB read (no API), MojoMojo is a usable read/projection/backup shard; with DB write it could be write-through, but carefully (app invariants). ### Divergences (boundaries / notes) - **No file store, no API** ⇒ the **direct-DB-read** binding is a first-class attach mode the contract must name (alongside file-store, in-engine host, external-API, CRDT, P2P) — or a sub-mode of "external store" where the medium is **a relational schema** (T14). Reading a third-party app's DB is **coupling to its schema** (versioned, may drift across MojoMojo versions) — a stated risk (UC-43 backend-swap analogue at the schema level). - **Writing by direct DB** risks violating app invariants (lineage, version counters, search index) — default to **read/projection/overlay**; write-through only with the app's cooperation. ### What to keep 1. **Direct-DB-read as a named binding** for DB-backed engines with no file/API (UC-81), mapping a **relational schema → wiki page model + journal**. 2. **DB version rows as a history source** for the journal (T13), beside git and RCS. 3. **Schema-coupling caution** — treat the schema as a versioned interface that can drift (relates UC-43). ## 5. UC seed | # | Seed | Disposition | |---|------|-------------| | UC-81 | Attach a **DB-backed wiki with no file store / no API** (MojoMojo) by reading its **relational store directly** (page + version tables), mapping schema → page model and **importing DB-resident history** to the journal | **new** | | — | DB attach vs file attach | enrich **UC-02** / **UC-40** | | — | DB version-table history import | enrich **UC-36** | | — | relational page rows / lineage as structure | enrich **UC-34** | ## 6. Architecture notes for SHARD-WP-0002 - **T14 (binding):** add **direct relational read** as a binding (or external-store sub-mode whose medium is a SQL schema) for DB-backed engines lacking a file store or API; HTML scrape is the lossy fallback. Schema is a **versioned coupling** (drift risk, UC-43). - **T13 (history portability):** **DB version rows** = a history source alongside git commits and RCS revisions — import as discrete journal entries (author/timestamp). - **T11 (capability):** "has-file-store" / "has-API" are **absent** here; "has-readable-DB" is the capability — a sparse profile relying on schema knowledge. ## 7. Open questions 1. Does shard-wiki sanction **direct third-party DB reads** as a binding, or restrict them (schema coupling/drift) to a documented best-effort mode? How is schema drift across MojoMojo versions handled (UC-43)? 2. Is **write-through by direct DB** ever allowed (risking app invariants), or are DB-backed no-API engines read/projection/overlay/backup only? ## 8. Sources - MojoMojo — github.com/mojomojo/mojomojo; metacpan MojoMojo (Catalyst app, DBIx::Class schema: Page / PageVersion / Content / Attachment) - Catalyst + DBIx::Class framework docs (architecture context) - prior: `research/260613-twiki-deep-dive/` (file-store classic contrast, UC-40/41) ## 9. Traceability New UC **UC-81** carries the marker **⊙** in the wikiengines column of `spec/UseCaseCatalog.md`. Enriched: UC-02, UC-40, UC-36, UC-34. Architecture cross-refs: SHARD-WP-0002 T14 (direct-DB binding), T13 (DB version-row history), T11.