Marvia

Sign inSign up

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

authorvalidateversionindexrelaterenderexport

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.

FieldTypePurpose
idContentIdStable identifier, never reused.
workspaceIdWorkspaceIdStructural tenant boundary; no cross-workspace reads.
typeContentTypeRegistry key that selects schema, template and permissions.
slugstringUnique per workspace, type and language.
titlestringRendered as the single H1 and the SEO title fallback.
excerptstringSummary for listings, search results and meta description.
bodyMarkdownMarkdown-first source; blocks are derived, never authored twice.
authorIdUserIdAttribution and audit.
statusContentStatusLifecycle state.
visibilityContentVisibilityAccess scope, validated against status.
languagestringBCP-47 tag; drives fallback and localized slugs.
versionnumberMonotonic; each publish freezes an immutable version.
metadataRecord<string, unknown>Type-specific fields declared by the type schema.
relationsContentRelation[]Links to content, reports, findings, projects, extensions.
seoContentSeoMeta, OpenGraph, canonical and schema.org output.
coverImagestring | nullAbsolute 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

projectIdtagscategoriescontributorsattachmentspublishAtreadingMinutes
StatusVisibilityRule
draftworkspaceprivateAuthor-only working copy; never indexed.
reviewworkspaceprivateLocked for reviewers; comments allowed, body frozen.
publishedpublicworkspacemembersprivateImmutable version pinned; edits create a new version.
archivedhiddenKept for audit and history, removed from every listing.
scheduledpublicPublishes 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

headingparagraphlistquote

media

imagegalleryvideoembed

interactive

tabsaccordiontablecodemermaid

ui

alertbadgebuttondividermetric-cardchart

learning

learning-cardcta-block

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

contentreportfindingprojectextensionlessonglossary
  • · 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

contentfindingreportlessonglossary-termai-memory

Edges

explainsreferencessupersedesrequiresbelongs-to

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.

coursemodulechapterlessonsection
  • · 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