# markdown-site: the sync step replaces the rebuild

> markdown-site keeps posts and pages as Markdown files in the repository and pushes them to a Convex backend with a single sync command, so a committed file is live without a rebuild or redeploy. The parts aimed at agents are unusual: a shell-like filesystem endpoint with no authentication, a set of documentation files that regenerate themselves with site statistics, and an MCP server.

**waynesutton/markdown-site** — 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.

- Repository: https://github.com/waynesutton/markdown-site
- Website: https://www.markdown.fast
- Stars: 628 · Forks: 91
- Language: TypeScript
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/waynesutton-markdown-site

## Sync is the deploy step

The publishing loop is three commands, and the middle one is the whole product:

```bash
# Edit, commit, sync
git add content/blog/my-post.md
git commit -m "Update post"
npm run sync        # dev
npm run sync:prod   # production
```

Posts and pages live in `content/blog/` and `content/pages/` as ordinary files in the git repository. That is stated as a deliberate property: you commit changes, review diffs and roll back like any codebase. The sync command then pushes the content to Convex, and the site updates with no rebuild and no redeploy.

The consequence is that the normal static site workflow disappears. There is no static build artefact to regenerate, no deploy to queue, and no point at which the repository and the live site disagree because a pipeline failed.

Real-time sync is the other half. Convex handles data sync, so all connected browsers update automatically, and several developers can each run the sync command from different machines without coordinating.

The stack behind it is React, Convex and Vite, with the project describing itself as optimised for answer engine optimisation, generative engine optimisation and LLM discovery.

## One command name, an environment variable for production

The script list is long but its shape is uniform, and the shape is worth understanding before you add to it.

Every command has a development form and a production form, and the production form is not a different script. In the manifest, `sync` runs a TypeScript script directly, while `sync:prod` prefixes the same command with `SYNC_ENV=production`. The wiki, discovery and export commands follow the same pattern.

There are four families. Content sync handles posts and pages. Wiki sync builds a searchable wiki from `content/blog` and `content/pages`, and accepts a `--kb=<id>` argument to send a wiki into one specific knowledge base rather than the default. Discovery sync regenerates the agent-facing files. Export goes the other way, pulling dashboard posts and pages back out into the content folders.

Two aggregate commands chain them: `sync:all` runs content, then wiki, then discovery, and `sync:all:prod` does the same with the production variants of all three.

That last point has a practical edge. Chaining the three development commands by hand is exactly the situation where someone forgets discovery and ships an article with a stale `llms.txt` pointing at a page that no longer exists.

## The filesystem endpoint has no authentication

The feature list includes a virtual filesystem: a shell-like HTTP interface for `ls`, `cat`, `grep` and `tree` across all site content, served at `/vfs/exec`, and the README says no auth is required.

That is a deliberate design choice aimed at agents, and it is the single most consequential line in the feature list. A model can enumerate and read a site's content with the same primitives it would use on a local machine, without being given a token or a session.

The output formats support the same goal from the other side. Content is available as JSON through API endpoints, as raw `.md` files, and as RSS feeds, so a consumer can pick whichever is cheapest rather than parsing HTML.

Anonymous demo mode is the other place authentication is deliberately absent. Visitors can explore the admin dashboard without signing in, and the demo content resets every thirty minutes, which bounds the exposure to generated content rather than to your own.

If your site holds anything you would not publish, the virtual filesystem endpoint is the line to think about before deploying.

## The agent-facing files regenerate with site statistics

The repository ships four sets of documentation written for AI, and they are not maintained by hand.

`CLAUDE.md` holds project instructions for the Claude Code CLI, covering workflows, commands and conventions. `AGENTS.md` holds general agent instructions about the codebase structure. `llms.txt` is served at `/llms.txt` as a discovery file. And a `.cursor/skills/` directory holds focused documents: frontmatter syntax and every field option, Convex patterns specific to this app, how the sync commands work and where content flows, plus two skill files for the `@robelest/convex-auth` integration patterns and for Convex static self hosting setup.

All of them are updated during `npm run sync:discovery`, with current site statistics written in as it goes. The discovery command also includes the wiki pages and copies `AGENTS.md` into `public/` so it is served.

The point of generating these files is that they go stale. A hand-written instruction file describing a project's commands and structure is wrong the moment the project changes, so this one is regenerated from the same source of truth as the content.

## Fork configuration is one JSON file with legacy modes

Configuring a fork is three steps, and the third is the interesting one because a script writes the settings for you:

```bash
cp fork-config.json.example fork-config.json
# Edit fork-config.json with your site info
npm run configure
```

The example file groups everything you can change. Site settings cover the name, title, description, URL and domain. Creator info covers a name, social links and a bio. Feature toggles cover the newsletter, the dashboard, a stats page and AI chat.

Three settings are enums with a legacy branch, and the default is always the current option rather than the old one. Auth mode is `convex-auth` by default, with `workos` described as legacy and `none` available for local development. Hosting mode is `convex-self-hosted` by default, with `netlify` marked legacy, which matches the older Netlify-oriented description of the project. Media provider is `convex` by default, with `convexfs` and an object store option.

Reading those three together tells you the project's direction: the defaults are the newest implementation of each, and the previous ones are still selectable so an existing deployment does not break on upgrade. `FORK_CONFIG.md` is the complete reference.

## Search, import and chat all run against the same content

Three features read the site's content rather than the filesystem, and they are built on different technologies.

Semantic search finds content by meaning rather than keyword, using OpenAI embeddings. Full text search is separate and keyboard-driven, with a command shortcut, result highlighting, and coverage across posts, pages and the wiki. Having both is not redundancy: embedding search finds the page that discusses a concept without using the word, while text search is the one you want when you know the string.

Ask AI is a chat interface over the site's own content, returning answers with sources, behind another keyboard shortcut.

URL content import works in the other direction, taking any webpage and scraping it into markdown through a hosted scraping service.

The wiki layer is what connects these. Wikis are built from your content, knowledge base projects can be created by uploading an Obsidian vault or a markdown folder with per-project API access, and a knowledge graph gives an interactive view of how wiki pages and knowledge bases connect to each other.

The admin dashboard is where content management, live preview, analytics, the config editor, sync buttons and knowledge base management all live, and it is also what the export command reads from.

## Markdown in the repository stays the source of truth

It is worth being precise about the direction of travel, because a dashboard and a repository both look like they could own the content.

The repository owns it. Files in `content/blog/` and `content/pages/` are the source, the sync command pushes them to the backend, and the dashboard edits content that lives there.

The export commands exist for the reverse case. `npm run export:db` pulls dashboard posts and pages back out into the content folders, with a production variant, which is what you run after making substantial edits through the interface so the changes land in git where they can be reviewed.

That asymmetry is the point of the design. A content edit made in a browser interface is easy to make and easy to lose; a content edit made in a file and committed is neither.

The remaining scripts fill in the operational edges: an import command for pulling a URL in, a configure command for forks, two newsletter senders, a server-side sync, and separate validate and verify commands for the environment and for a deployment, each with a production variant.

## The tree ships the configuration for the tools it uses

The top level is a small site framework plus a surprising amount of agent tooling, and reading the file names tells you how the project expects to be worked on.

There are configuration directories for three assistants, alongside `CLAUDE.md`, `AGENTS.md` and `opencode.json`, plus a `skills-lock.json` that pins the skill files the same way a lockfile pins dependencies. That is a deliberate parallel to the package lockfile, and it is the thing to check before pulling a skill change.

There is also a `prds/` directory for product requirements, a `TASK.md`, a `files.md` and a `convex-doctor.toml`, which reads like a configuration for a diagnostic tool rather than a note.

Two entries are odd enough to mention. There is a directory named `npx` and a file named `markdown-site@1.0.0` sitting at the repository root, neither of which belongs in a project whose own manifest says version 1.0.1. Both look like accidental writes from a command run in the wrong directory, and neither is referenced by anything documented.

The Convex side has its own directories, with the virtual filesystem implementation separated from the rest, and there is a `netlify/` directory alongside `netlify.toml` for the legacy hosting mode.

## Conclusion

Use markdown-site if you want content you can review in a diff and an agent that can read the same site over HTTP, because the Markdown files and the unauthenticated filesystem endpoint are the two things it does that a static generator does not. Pass if you want a static build with no backend, since this design assumes a Convex deployment running. Before you fork, read the fork configuration rather than editing settings in the dashboard, and decide what you want public, because the virtual filesystem endpoint requires no authentication.

## FAQ

### What is markdown-site?

An MIT licensed TypeScript publishing framework for sites, documentation and blogs, built with React, Convex and Vite. Content lives as Markdown files in the repository, and a sync command pushes it to a Convex backend so the live site updates without a rebuild or redeploy.

### How do I publish content with markdown-site?

Write Markdown in content/blog or content/pages, commit it, then run npm run sync for development or npm run sync:prod for production. Convex handles real-time sync, so connected browsers update automatically and several developers can each sync from their own machine.

### What agent-facing files does markdown-site generate?

AGENTS.md for general agents, CLAUDE.md for the Claude Code CLI, llms.txt served at /llms.txt, and a .cursor/skills directory covering frontmatter, Convex patterns, sync behaviour and two integrations. They are regenerated by npm run sync:discovery with current site statistics.

### What is the /vfs/exec endpoint in markdown-site?

A shell-like HTTP interface offering ls, cat, grep and tree across all site content, so an agent can browse a site the way it would browse a directory. The README states that no authentication is required.

### How do I configure a fork of markdown-site?

Copy fork-config.json.example to fork-config.json, edit it with your site details, and run npm run configure. It covers site and creator details plus feature toggles, and three enums whose defaults are the current options: auth mode convex-auth, hosting mode convex-self-hosted, and media provider convex.

## Sources

- [Issues](https://github.com/waynesutton/markdown-site/issues)
- [License: MIT](https://github.com/waynesutton/markdown-site/blob/main/LICENSE)
- [Project website](https://www.markdown.fast)
- [README](https://github.com/waynesutton/markdown-site/blob/main/README.md)
- [waynesutton/markdown-site on GitHub](https://github.com/waynesutton/markdown-site)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/waynesutton-markdown-site
