Marvia-12
Write once. Render everywhere.
Every content experience — blog, documentation, academy, playbooks, policies, AI knowledge — is one record, one editor and one renderer. Adding a type never means adding a page.
Content schema
1.0
Registered types
18
Components
21
Templates
8
Content pipeline
Single source
One editor, one renderer, one schema for every content experience.
Schema-driven
Content types are JSON schema registrations, not components.
Component-based
Every block maps to a registered design-system component.
Versioned
Immutable versions with diff and rollback for anything published.
Markdown-first
Markdown plus extensions is the stored source; HTML is output only.
AI-ready
Structured output feeds the AI layer without scraping the UI.
SEO-optimized
Meta, OpenGraph, schema.org and sitemaps are derived, not authored.
Content model
One record for every experience
Core fields are mandatory everywhere; type-specific fields live in metadata declared by the type schema.
| Field | Type | Purpose |
|---|---|---|
| id | ContentId | Stable identifier, never reused. |
| workspaceId | WorkspaceId | Structural tenant boundary; no cross-workspace reads. |
| type | ContentType | Registry key that selects schema, template and permissions. |
| slug | string | Unique per workspace, type and language. |
| title | string | Rendered as the single H1 and the SEO title fallback. |
| excerpt | string | Summary for listings, search results and meta description. |
| body | Markdown | Markdown-first source; blocks are derived, never authored twice. |
| authorId | UserId | Attribution and audit. |
| status | ContentStatus | Lifecycle state. |
| visibility | ContentVisibility | Access scope, validated against status. |
| language | string | BCP-47 tag; drives fallback and localized slugs. |
| version | number | Monotonic; each publish freezes an immutable version. |
| metadata | Record<string, unknown> | Type-specific fields declared by the type schema. |
| relations | ContentRelation[] | Links to content, reports, findings, projects, extensions. |
| seo | ContentSeo | Meta, OpenGraph, canonical and schema.org output. |
| coverImage | string | null | Absolute URL used for hero and og:image. |
{
"schemaVersion": "1.0",
"id": "cnt_01H...",
"workspaceId": "ws_01H...",
"type": "lesson",
"slug": "how-marvia-analyzes-a-website",
"title": "How Marvia analyzes a website",
"excerpt": "Snapshot first, findings second, report last.",
"body": "# ...markdown...",
"status": "published",
"visibility": "public",
"language": "en",
"version": 4,
"metadata": { "courseId": "crs_101", "durationMinutes": 8 },
"relations": [
{ "kind": "report", "targetId": "rep_01H...", "label": "Example report", "direction": "outbound" }
],
"seo": { "title": "...", "ogImage": "https://...", "noindex": false },
"coverImage": "https://..."
}Optional fields
| Status | Visibility | Rule |
|---|---|---|
| draft | workspaceprivate | Author-only working copy; never indexed. |
| review | workspaceprivate | Locked for reviewers; comments allowed, body frozen. |
| published | publicworkspacemembersprivate | Immutable version pinned; edits create a new version. |
| archived | hidden | Kept for audit and history, removed from every listing. |
| scheduled | public | Publishes automatically at publishAt; hidden until then. |
Registry & templates
Types are data, not components
Every type registers a template, a permission and its schema fields. Extensions register into the same table.
Primary types
- blogarticletags, coverImage, readingMinutes
- docsdocumentationsection, order, toc
- lessonlessoncourseId, moduleId, durationMinutes
- guidedocumentationdifficulty, toc
- tutorialdocumentationsteps, prerequisites
- playbookplaybookstages, owner
- knowledgedocumentationtopic, aiIndexed
- api-docsreferenceendpoint, method, version
- changelogtimelinereleaseVersion, date
Specialized types
- case-studyarticlecustomer, metrics
- roadmap-entrytimelinequarter, state
- faqfaqquestion, answer
- glossaryreferenceterm, aliases
- extension-docsdocumentationextensionId, version
- marketplace-docsdocumentationlistingId
- policylegaleffectiveFrom, jurisdiction
- release-notetimelinereleaseVersion, highlights
- announcementarticleaudience, expiresAt
Registry rules
- · No content type may be hardcoded in a component; the registry is the only source.
- · A type registration must declare template, permission and schema fields.
- · Extensions register types at load time and may never override a core type.
- · Unknown types render through the default article template instead of failing.
Templates
- article — cover → header → body → related → cta
- documentation — breadcrumb → header → toc → body → related → feedback
- lesson — header → objectives → body → summary → next-lesson
- playbook — header → stages → body → checklist → related
- reference — header → signature → parameters → examples → related
- timeline — header → entries → body
- faq — header → questions → related
- legal — header → effective-date → body → history
Generated navigation
- Sidebar — Type registry plus metadata.section and metadata.order.
- Breadcrumbs — Type → category → slug path.
- Table of contents — Heading blocks with anchor ids.
- Related content — Relation engine and shared tags.
- Prev / next — Ordered siblings within a section or module.
Renderer & editor
One renderer, one editor, many types
Blocks map to registered design-system components. The editor is shared by blog posts, lessons and policies alike.
Renderer supports
- · Markdown with GFM tables, task lists and footnotes
- · Callouts and admonitions (warning, tip, info, success)
- · Cards, code blocks, tabs and accordions
- · Images, galleries, videos and allow-listed embeds
- · Mermaid diagrams rendered from source text
- · Design-system components: alerts, badges, metrics, charts
- · HTML is never stored; Markdown and blocks are the only source of truth.
- · Sanitization runs before render — no raw HTML from untrusted authors.
- · Every image requires alt text; the renderer refuses to emit an empty alt.
- · Rendering a 10k-word document must stay under 500ms.
Editor
- Split view — Markdown source beside the live universal renderer.
- Auto-save — Debounced drafts every few seconds; never blocks typing.
- Version history — Immutable versions with diff and one-click rollback.
- Slash commands — Insert any registered component by name.
- Drag and drop — Uploads go to object storage and return a block, not a data URL.
- Reusable blocks — Saved snippets shared across content types.
- Templates — Type-specific starting structures from the template engine.
- AI assist — Suggestions only; published content is never modified automatically.
Component library
text
media
interactive
ui
learning
Knowledge hub
Search, relations and the graph
Content joins findings, reports and lessons in one graph so the AI and the UI read the same structure.
Search
- Full-textavailableTitle, excerpt, body and metadata, scoped to the workspace.
- FiltersavailableType, tag, category, language, status and author.
- SemanticplannedEmbedding search across content, findings and lessons.
- Universal searchavailableContent is one scope inside the shell's search provider set.
Relation engine
- · Relations connect content to content, reports, findings, projects and extensions.
- · A relation is stored once and rendered from both sides via direction.
- · Extensions register relation handlers; they never write into core tables.
- · Broken relations degrade to a disabled reference, never to a crash.
Knowledge graph
Nodes
Edges
AI integration
- Content body and metadata → Summaries and TL;DR blocks (drafts only)
- Related findings → Contextual explanations inside reports
- Learning hierarchy → Next-lesson recommendations
- Glossary terms → Inline definitions and translations
AI safety
- · AI never modifies published content; it can only propose a draft version.
- · Every AI-generated block is attributed and reviewable before publish.
- · Private and policy content is excluded from cross-workspace AI context.
Learning framework
Courses are content, not a second engine
Course → Module → Chapter → Lesson → Section, all rendered by the same pipeline.
- · A lesson is a content record; the hierarchy lives in metadata and relations.
- · Progress is tracked per user per lesson and rolls up to module and course.
- · Quizzes, assignments and certificates extend the same record, not a new engine.
- · Academy pages reuse the universal renderer — no bespoke lesson component.
Versioning, SDK & export
Immutable history, one SDK, four exports
Publishing freezes a version; extensions extend through registrations; exports read the exact object the UI reads.
Versioning & localization
- · A published version is immutable; the next edit opens version n+1 as a draft.
- · Each version stores a content hash so tampering is detectable.
- · Rollback creates a new version whose body equals an earlier one; nothing is deleted.
- · Diffs are computed on Markdown, so history stays readable and reviewable.
- · Audit logs record author, timestamp and reason for every transition.
- · Every record carries a BCP-47 language tag; the default is the workspace locale.
- · Translations share a translationGroupId and each keeps its own slug and SEO.
- · Missing translations fall back to the workspace default and are marked as such.
- · Language is part of the slug uniqueness key: (workspace, type, language, slug).
Content SDK
- createContent(type, data) — Create a record of any registered type, validated against its schema.
- registerContentType(schema) — Add a new type with template, permission and fields.
- registerRenderer(component) — Extend rendering for a block type without forking the renderer.
- registerTemplate(template) — Add a reusable section order for one or more types.
- registerComponent(component) — Publish a UI component into the schema-driven library.
- registerRelation(type, handler) — Connect content to external objects such as findings.
- registerKnowledge(indexer) — Feed the knowledge graph and search index.
- · Extensions contribute schema and components; the engine owns layout and routing.
- · A registration is rejected if the manifest lacks the matching permission.
- · Core types and core templates may be read but never replaced.
- · SDK calls happen at load time only — no registration during render.
Export
- markdownavailableRound-trips the stored source exactly.
- jsonavailableBlocks plus metadata for machines and AI.
- htmlavailableSanitized static rendering for embedding.
- pdfavailablePrint-ready rendering of the same object the UI reads.
- epubplannedCourse and handbook bundles.
- docxplannedEditorial review outside the platform.
Permissions
- owner / admin — Create, publish, archive and manage every type.
- editor — Create and edit; publishing requires review for reviewed types.
- viewer — Read workspace and member content.
- guest — Read public content only.
- extension — Register types, templates, components and relations within granted scopes.
Non-functional requirements
- Performance — Render under 500ms for a 10k-word document.
- Scalability — 10k+ concurrent readers on cached published content.
- Security — Role-based access on every read plus full audit logs.
- Accessibility — WCAG 2.1 AA: headings, alt text, focus order, contrast.
- Extensibility — No core modification required to add a content type.
Roadmap
- · Semantic search across content, findings and lessons
- · AI-assisted drafting with mandatory human review
- · Mobile-optimized rendering profile
- · Offline content packs
- · Content usage analytics