Model or dataset
waynesutton/markdown-site avatar
waynesutton/markdown-site

markdown-site: A Convex-Backed Publishing Framework With a Sync Step Instead of a Build

An open-source publishing framework built for AI agents and developers to ship websites, docs, or blogs. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify.

630 stars91 forksTypeScriptMIT

At a glance

What is it?
waynesutton/markdown-site stores posts as markdown in git and pushes them to Convex with npm run sync, so publishing skips a rebuild. The trade is a backend dependency and a self-hosting decision that the README leaves partly open.
Who is it for?
Adopt markdown-site if your team already writes in markdown and you accept Convex as the runtime for both data and, by default, hosting. Skip it if you need a static export with no backend, or if you cannot operate a Convex deployment in production.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 119 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The publishing step markdown-site removes, and the one it adds

Most markdown blogs treat the build as the publish. You edit a file, commit, and a CI job regenerates HTML. Content only changes when that pipeline finishes, and a typo fix means a full deploy. markdown-site inverts this. Markdown lives in content/blog/ and content/pages/ as ordinary files in your git repository, and the README states that you run npm run sync for development or npm run sync:prod for production, after which content appears on the live site immediately, with no rebuild or redeploy. Convex handles the real-time data sync, so browsers connected to the site update on their own.

The audience is narrow but real. It fits teams that want git as the source of truth for prose and a database as the delivery layer, and that are willing to run a Convex backend to get it. The README also positions the project for AI consumption: content is meant to be reachable by browsers, LLMs and agents, with llms.txt, AGENTS.md and CLAUDE.md generated alongside the site. If your publishing problem is 'editors wait on CI' or 'agents cannot read our docs cleanly', this addresses both. If your problem is 'we want a folder of static files on a CDN', it does not.

What happens between npm run sync and a browser update

The sync scripts live in scripts/, named sync-posts.ts and sync-discovery-files.ts in the README. Running npm run sync executes the content script against Convex, which writes the parsed markdown into the backend. From there, Convex subscriptions push changes to every connected client, which is why the README can promise instant updates across browsers without a deploy. The frontend is React with Vite, so the rendering layer is a normal single-page app reading from Convex queries.

The same command family covers three separate concerns. npm run sync:wiki builds wiki pages out of content/blog and content/pages. npm run sync:discovery regenerates AGENTS.md, CLAUDE.md and llms.txt, and copies AGENTS.md into public/, which means the discovery files are build artifacts of your content rather than hand-maintained documents. npm run sync:all runs content, wiki and discovery together, with :prod variants for each. The wiki sync accepts a --kb=<id> flag to target a specific knowledge base, which implies knowledge bases are addressable entities inside the deployment rather than a single global wiki.

Two features sit outside the sync path. The virtual filesystem exposes ls, cat, grep and tree over HTTP at /vfs/exec, with no authentication required according to the README, which is worth reading twice before you enable it on a site with private drafts. URL import uses Firecrawl to scrape a page into markdown. Both are conveniences layered on the same content store.

Fork configuration is a single JSON file, and it decides your architecture

The README gives an explicit setup sequence. You copy fork-config.json.example to fork-config.json, edit it with your site details, and run npm run configure. That file is not cosmetic. It carries site name, title, description, URL and domain, plus three choices that change what you are operating: auth.mode, hosting.mode and the media provider.

Auth has three values. convex-auth is the default, workos is described as legacy and selected with auth.mode: "workos" in siteConfig, and none exists for local development. Hosting has two: convex-self-hosted as the default and netlify as legacy. Media can be convex, convexfs or r2. The defaults matter because a fork that never touches fork-config.json lands on Convex for auth, hosting and media. The README also points to FORK_CONFIG.md as the complete reference and to a hosted fork configuration guide, so the example file plus those two documents are the authoritative surface.

The rest of the file covers creator name, social links and bio, and feature toggles for newsletter, dashboard, stats page and AI chat. The practical consequence is that turning off the dashboard or AI chat is a config edit and a re-run of the configure step, not a code change. That is a reasonable design for a template repository, though it does mean your deployment's shape is described in one file that reviewers should read in pull requests.

Where the README is thin, and where the design constrains you

The material does not describe what happens when a sync fails halfway, whether writes are transactional across a batch of posts, or how deletions are handled. If you remove a markdown file and run npm run sync, the README does not say whether the corresponding Convex record is removed or orphaned. That is the first thing to test on a throwaway deployment, because a content store that accumulates ghosts is a different operational burden from one that mirrors the repository.

There is also a two-way door the README opens without resolving it. The dashboard can create posts and pages, and npm run export:db writes dashboard content back to content folders, with a :prod variant. So content can flow from git to Convex and from Convex to git. Nothing in the supplied material states which direction wins on conflict, or whether the export overwrites existing files. A team that lets editors publish from the dashboard while developers commit to content/blog/ is running two writers against one store, and the reconciliation rule is not documented here.

The virtual filesystem deserves the same caution. The README says /vfs/exec requires no auth. If your site holds unpublished drafts in the same content store, an unauthenticated shell-like read interface over that store is a decision to make deliberately, not a default to leave on.

Finally, the self-hosting default is not free. Choosing convex-self-hosted means you operate the backend that serves your pages. That is the opposite of the static-site bargain, where the worst outage is a stale CDN edge.

How it differs from a static site generator

Astro, Eleventy and Hugo also start from markdown in a repository. The difference is where the content lives after you write it. In those tools, markdown is compiled at build time into files that are then served; the site is the artifact, and the database, if any, is optional and separate. In markdown-site, markdown is input to a sync process that writes into Convex, and the served page is a query result. Publishing is a write to a live datastore rather than a regeneration of files.

That single change propagates. Real-time updates across browsers come free, because the client is already subscribed. Rollback is a git revert plus a sync, which the README frames as reviewing diffs and rolling back like any codebase. But you cannot hand someone a tarball of the built site, and you cannot host the output on object storage without the Convex backend answering queries. If your deployment target is a bucket and a CDN, this framework is the wrong shape, not a worse version of the right one.

The AI-facing pieces are a second axis of difference. Generating llms.txt, AGENTS.md and CLAUDE.md as part of npm run sync:discovery, and exposing raw .md files and JSON endpoints, is a deliberate bet that machine readers are first-class consumers. Static generators can serve raw markdown too, but here it is wired into the same command that publishes the human-readable site, so the two cannot drift apart silently.

Maintenance surface and licence terms

The repository is TypeScript, MIT licensed, and the README shows no releases retrieved in the supplied material, so there is no versioned changelog to plan upgrades against. You track main. The dependencies you inherit are React 18, Vite, Convex, and whatever the configure step wires up for auth, media and hosting. The README names @robelest/convex-auth for the default auth path and includes a skill document for its integration patterns, which tells you the auth layer is a third-party package you will need to follow separately.

Upgrade cost concentrates in fork-config.json and the Convex schema. Changing auth.mode or hosting.mode after launch is a migration, not a toggle, because users and stored files are attached to the previous choice. The sync scripts are the other maintenance point: they are your code once forked, and they encode the frontmatter contract that the .cursor/skills/frontmatter.md document describes. Adding a frontmatter field means touching the script, the skill doc and any rendering that reads it.

MIT means you can fork, modify and ship commercially, and the only obligation is preserving the licence and copyright notice. That is the entirety of the licence implication here; nothing in the supplied material suggests additional terms, and this is not legal advice.

Who should fork this, and what to check before you do

Fork it if you want markdown in git as the authoring format, a live backend as the delivery layer, and machine-readable outputs generated by the same command that publishes the site. The combination of npm run sync:all, the discovery file generation and the MCP server endpoint is coherent for documentation sites that need to serve both people and agents. Teams already comfortable running Convex will find the setup short: copy fork-config.json.example, edit it, run npm run configure, then npm run sync:prod.

Do not fork it if a static export is a requirement, if you cannot operate a Convex deployment, or if you intend to let both dashboard editors and git committers write content before you have established which side wins. The export:db command exists, but the conflict rule is not in the material provided.

Verify three things on a scratch deployment before migrating real content. First, delete a post locally, run npm run sync, and check whether the Convex record disappears. Second, confirm that npm run sync:prod points at your production Convex instance and not the dev one, since the two commands differ by a suffix and the mistake is silent. Third, decide the auth.mode and hosting.mode values up front, because the README treats convex-auth and convex-self-hosted as defaults and workos and netlify as legacy, and switching later means moving users and files rather than editing a string.

Editorial conclusion

Adopt markdown-site if your team already writes in markdown and you accept Convex as the runtime for both data and, by default, hosting. Skip it if you need a static export with no backend, or if you cannot operate a Convex deployment in production. Before committing, verify the auth.mode and hosting.mode values in fork-config.json against your deployment, and confirm that npm run sync:prod targets the production Convex instance rather than a dev one.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. waynesutton/markdown-site on GitHub
Community notes

Community notes