Documentation

Everything you need to build, govern, and deploy sites with Stablemount.

Installation

Stablemount requires Node.js 20+ and an Anthropic API key.

git clone https://github.com/OWNER/stablemount.git
cd stablemount
npm install

Create a .env file in the project root:

ANTHROPIC_API_KEY=sk-ant-your-key-here
PORT=3000

Start the server:

npm start

This runs node --watch server.js, which auto-restarts on file changes. For production, use npm run start:no-watch.

Open http://localhost:3000/dashboard to begin.

Your first page

From the dashboard, click "+ Generate Page". Give it a title and describe what you want:

Example intent: "Landing page for a sustainable coffee roastery. Hero section with headline and CTA, menu highlights grid with images, an about section with the story, and a final call to action."

The AI returns:

Everything is saved as flat files in content/. Click "View & Edit" to open the page with the editing overlay.

Editing pages

The editing overlay appears on every page. It provides three ways to edit:

Inline text editing

Click any element with a data-content attribute to edit it directly. A formatting toolbar appears with bold, italic, link, heading levels, and list options. Press Enter to split paragraphs, Backspace at the start to merge.

Conversational AI

Click the AI button in the top bar to open the chat panel. Describe changes in natural language: "Make the hero section darker", "Add a testimonials section after features", "Change the CTA text to Get Started Free".

The AI makes surgical string-replacement patches to the page HTML. It can also create pages, collections, entries, and server functions in a single response.

Section-level prompts

Click any section or partial on the page to select it. A context bar appears at the bottom where you can type a prompt scoped to just that section. The AI sees the selected HTML and focuses changes there.

Click Save (or check the unsaved badge) to persist changes to disk. A version snapshot is created automatically on every save.

Decisions

Decisions are named constraints that govern AI behavior across your site. Every time the AI generates a page, it proposes decisions. You can also create them manually.

Kinds

KindWhat it isExample
TokenA CSS value the AI must usePrimary Color: #4f46e5
InstructionProse guidance for the AI"Use a warm, conversational tone. Never use corporate jargon."
AssetA reference to a media fileLogo: /media/uploads/logo.svg

Weight

Scope

Tokens are automatically converted to CSS custom properties and injected into the AI's system prompt.

Components

Components come in two types:

Partials

Reusable HTML bundles injected server-side via directives. A page's HTML contains only the directive comment — the server resolves it at render time:

<!-- @partial:header -->
<main>...page content...</main>
<!-- @partial:footer -->

Partials are self-contained bundles: HTML first, then a <style> block, then an optional <script> block. Edit a partial once and it updates across every page that uses it.

Patterns

Reference templates the AI uses as guidance when generating similar elements. Unlike partials, patterns are not injected into pages. They teach the AI your preferred patterns — card layouts, hero structures, pricing tables — so it maintains consistency.

Both types have weight (rule/guide) and scope (global/page/collection) just like decisions.

Collections

Collections are structured content types. Each collection has a schema (typed fields) and entries.

// Example: Blog collection schema
[
  { "name": "title", "type": "text", "required": true },
  { "name": "excerpt", "type": "text" },
  { "name": "body", "type": "richtext", "required": true },
  { "name": "image", "type": "image" },
  { "name": "published", "type": "boolean" }
]

After creating a collection and adding entries, click "Generate Templates" to have the AI create listing and detail page templates.

Embed collections in any page using directives:

<!-- @collection:blog limit=3 sort=created order=desc -->
  <article data-each-entry>
    <h2>{{title}}</h2>
    <p>{{excerpt}}</p>
    <a href="{{entry.slug}}">Read more</a>
  </article>
<!-- @/collection:blog -->

Collections are served at clean URLs: /blog for listing, /blog/my-post for detail.

Functions

Sandboxed server-side logic. Functions live in content/functions/ and are served at /api/fn/{name}.

// content/functions/contact.js
async function POST({ body, query }) {
  await store.create('submissions', {
    slug: body.email,
    data: body
  });
  return { success: true };
}

Functions run in a Node.js VM sandbox with three APIs:

APIMethods
storelist(col), get(col, slug), create(col, {slug, data}), collections(), site()
httpget(url), post(url, body), put(url, body), delete(url)
envget(key)

No filesystem access, no imports, no require. Functions are safe enough for AI to generate.

File structure

stablemount/ server.js # HTTP server, all routes export.js # Static site export CLI package.json .env # API key (not committed) core/ store.js # All flat-file CRUD ai.js # Anthropic API integration partial.js # Partial directive resolution collection.js # Collection rendering images.js # Image localization functions.js # Function sandbox execution extensions.js # Extension loader auth/ login.html # Login / password setup page dashboard/ index.html # Dashboard shell app.js # Dashboard client-side app styles.css # Dashboard styles overlay/ overlay.js # Inline editing overlay overlay.css # Overlay styles extensions/ # Developer extensions (full Node.js) content/ # All user data (created at runtime)

Directives

Stablemount uses HTML comment directives that are resolved server-side at render time and stripped during export.

Partial directives

<!-- @partial:header -->

Replaced with the HTML content of the partial named "header". The partial's <style> and <script> blocks are aggregated and injected once per page.

Collection directives

<!-- @collection:blog limit=3 sort=created order=desc -->
  ...template with {{field}} placeholders...
<!-- @/collection:blog -->

Modes: dynamic (limit/sort/order) or curated (entries=slug1,slug2). The template element with data-each-entry is cloned for each entry.

Security

Static export

npm run export

This:

  1. Iterates all published pages (or all pages if none are published)
  2. Resolves partial and collection directives inline
  3. Strips all editing overlay artifacts (data-content, data-section, etc.)
  4. Copies media files to dist/media/
  5. Writes clean HTML to dist/ with clean URL structure

The output in dist/ is a fully static site you can deploy to any host — Cloudflare Pages, Netlify, Vercel, S3, or a plain web server.

Production setup

Stablemount is a single Node.js process. Recommended production setup:

# Using PM2
pm2 start server.js --name stablemount

# Using systemd
# Create a service file at /etc/systemd/system/stablemount.service

# Using Docker
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Always put a reverse proxy in front for HTTPS. Caddy is the simplest option:

# Caddyfile
yourdomain.com {
  reverse_proxy localhost:3000
}

Extensions

Extensions are full Node.js modules in extensions/*/extension.js. They have access to everything the sandbox does not: the filesystem, npm packages, external services.

// extensions/my-ext/extension.js
export default {
  id: 'my-ext',
  name: 'My Extension',

  routes: {
    '/my-endpoint': {
      GET: async ({ query }) => {
        return { body: { message: 'Hello from extension' } };
      }
    }
  },

  hooks: {
    onPageSave: async ({ slug, html }) => {
      console.log(`Page saved: ${slug}`);
    }
  }
};

Extension routes are served under /x/. They are hot-reloaded when the file changes — no server restart needed.

See extensions/example/extension.js for a complete example.