Publish a space, folder or note as a static documentation site, from the web app, the terminal or CI
A space, a folder or a single note can be published as a static documentation site. Every note becomes a page, folders become sections of the sidebar, and the site gets search, a dark mode, a table of contents on each page and files that AI agents can read.
A site is a snapshot: it is built from the notes as they are when you publish or rebuild it. When the notes change, rebuild the site to show the changes.
Publishing, rebuilding and unpublishing need admin rights in the space. A small globe next to a space, folder or note in the sidebar shows that it is published.
In the sidebar, right-click the space, folder or note (or press and hold it on a touch screen) and choose Publish as site.
Under Publish to, choose a target. Kribble (served by this server) is always there. The other choices appear only if the server's operator set them up (see Targets below). If this server is the only target, the dialog doesn't ask.
For this server, edit Site URL: the part after /p/<organisation>/ is the slug, made from the name. Use lowercase letters, numbers and hyphens. For a local directory or SSH server, set Subdirectory instead.
Turn on Group root folders as top-bar tabs to show each top-level folder as a tab in the site's header. Open Language & versioning if top-level folders hold translations or older versions (see Tabs, versions and languages below).
Appearance sets the site's colours (see Colours below). Access decides who can read it: Public or Space members only. Sites on other targets are always public.
Click Publish site. The build runs on the server in the background; the dialog shows its status and a message when the site is up.
Target | Where the site ends up | Who can read it |
|---|---|---|
Kribble (served by this server) |
| Public, or space members only |
Local copy | A directory on the server, under a base directory the operator chose, in the Subdirectory you give. Another web server serves it. | Public |
SSH upload | A directory on another machine, uploaded over SFTP, in the Subdirectory you give | Public |
Cloudflare Pages | A Pages project named after the space, folder or note, at | Public |
For local and SSH targets, the subdirectory is emptied and filled again on every build, so don't point it at a directory that holds anything else. Nested paths such as products/sdk/v2 work. Kribble doesn't know the public address of a site on a local or SSH target: ask the operator for it.
The operator sets up targets in the server's configuration: see Publish targets.
These options are for spaces and folders, and are set when you first publish.
Group root folders as top-bar tabs: each top-level folder becomes a tab in the header, and the sidebar shows only the active tab's pages. Good for docs with distinct parts, such as Guides and API reference.
Version folders: top-level folders that hold older versions of the docs, for example v1 and v2. The site gets a version switcher; the rest of the content is the latest version. Add a folder named latest to the list to make that folder the version shown at the site's root.
Language folders: top-level folders that hold translations, for example de and ja. The site gets a language switcher. Default language label names the language of the rest of the content (en if you leave it empty).
With tabs on, version and language folders are looked for inside each tab's folder. A language folder can also sit inside a version folder.
When folder names look like versions (v1, 2.0, latest, beta…) or language codes (de, ja, pt-br…), the dialog offers Auto-detect from folder names, which fills both lists for you.
Appearance has three colours: Brand (links, highlights), Light BG and Dark BG (the page background in light and dark mode). Left empty, the site uses the organisation's colours from its Theme settings. A change to the organisation's theme reaches a site at its next rebuild.
Open Publish as site again on the published space, folder or note. The dialog now shows:
The status: queued, building, published, failed, or transfer failed when the site was built but couldn't be copied to its target. A transfer error shows the message from the target; fix the target and click Retry transfer.
Site URL with Copy and an open button, for sites on this server. For a Cloudflare Pages site, its pages.dev address; for local and SSH targets, the target and subdirectory.
Access (sites on this server) and Theme. Changes to these apply at the next rebuild.
Last built and the time.
Rebuild: builds the site again from the current notes, with the access and theme shown.
Unpublish: takes the site down and removes its files from the target.
The slug, the target, the tabs and the version and language folders can't be changed on a published site. To change them, unpublish it and publish it again.
Readers get:
Search across all pages, with the search button in the header or Ctrl+K (Cmd+K on a Mac). It works like the app's search, in the reader's browser:
Titles match as you type, even from a few letters (dsk ap finds "The desktop app"); other words forgive a typo and complete the word being typed.
Each page's language is detected when the site is built, and words also find their other forms in it (deployments finds "deploying", beállításaid finds "Beállítások"), in all the languages the site's pages are in, for the same languages as the app's search. Longer words (six letters or more) also match inside longer words, which helps with compounds: korlátozás finds "sebességkorlátozásokat". Pages found these ways come after the pages that have the words as typed. Typed accents count as in the app: kör ranks a page with kör before pages with kor. The stemmers are small scripts published with the site, loaded only for the languages it uses.
Each result names the section that matched, and opens the page at that heading. A preview of the section shows on wide screens.
With nothing typed, it lists the pages the reader opened lately.
Operators narrow it down: in:Guides (a folder or tab, in:Guides/Setup for a path; Tab completes the name), title:word, updated:<30d (also today, >90d, 2026-03, 2026-01..2026-03), sort:recent, oldest or title, "exact phrase" and -without. An operator that can't match says so, and finds nothing.
↑ and ↓ move, Enter opens, Ctrl+Enter (Cmd+Enter on a Mac) opens in a new tab, Esc closes.
Sites of a single note have no search.
Dark mode, from the reader's system setting or the toggle in the header.
A table of contents of the page's headings next to the text, following the reader as they scroll.
The sidebar with every page (a menu button on phones), and the version and language switchers if the site has them.
The organisation's icon in the header, if it has one.
A copy button on every code block.
Above each page's title, Copy page copies the page as markdown, ready to paste into a chat with an AI. The arrow next to it opens more:
Copy as markdown and View as markdown (the page as plain text).
Use with your agent: the command that gives Claude Code, Codex or Claude Desktop these docs, with a copy button:
kribble mcp install --docs https://kribble.example.com/p/acme/sdk/
On a public site it also links the site's llms.txt. On a private site it says to sign the CLI in to the server first.
Every build writes, next to the HTML pages:
File | What it holds |
|---|---|
| The page as markdown; links to other pages point at their |
| The list of pages, linked as markdown (llmstxt.org) |
| Every page in one file; one per version and language |
| The pages, versions, languages and search indexes, for tools such as |
They are plain static files, so they work wherever the site is hosted: this server, your own web server, Cloudflare Pages.
Anyone with the kribble CLI can give their AI tools a published site, without an account on your server:
kribble mcp install --docs https://docs.example.com
This adds an MCP server to Claude Code, Claude Desktop and Codex (those it finds; name one to pick it, for example kribble mcp install codex --docs URL). Their agents can then search the docs, list the pages and read a page or one section of it. The server is named after the site (for example acme-sdk-docs); remove it with kribble mcp uninstall --name acme-sdk-docs.
To run the same server by hand, for another MCP client, use kribble mcp docs URL. It speaks MCP over standard input and output; --version v1 and --lang de pick the version and language it reads by default.
It reads only the site's public files, so it works with any host, and with sites built by other tools that have an llms.txt. For a private site, the reader must be a member of the space and have signed the CLI in to the same server with kribble login; the CLI sends that login only to that server.
The kribble CLI publishes and rebuilds sites with your login.
kribble targets # where sites can go, and the flags each takes
kribble deploy "Engineering/SDK" # publish; in a terminal it asks for the target
kribble sites # every site you can reach, with URL and status
kribble sites "Engineering/SDK" # one site
kribble redeploy "Engineering/SDK" --wait # build it again and wait for the result
kribble deploy PATH publishes a space ("Space"), folder ("Space/Folder") or note. If it is published already, it builds it again with its current settings and ignores the other flags. In a terminal, without --target, it asks where the site goes, then for the slug and access (this server) or the subdirectory (local and SSH). Its flags:
Flag | Meaning |
|---|---|
| The target type; |
| Which local, SSH or Cloudflare Pages target, when there are several |
| This server: the URL is |
| This server: who can read it (default |
| Local and SSH: the directory under the target's base (default: from the name); emptied on every build |
| Show the top-level folders as tabs |
| Top-level folders that are older versions |
| Top-level folders that are translations |
| The language of the rest |
| Wait until the build is done; exit with an error if it failed |
| Don't ask; use the defaults |
| Print the result as JSON |
kribble redeploy takes the published path or a site id (from kribble sites --json). Without --wait, both commands return as soon as the build is queued; kribble sites PATH shows when it is done.
kribble deploy never asks anything in CI: it asks for the target only in an interactive terminal, and not when the CI environment variable is set (GitHub Actions, GitLab CI and most others set it) or with -y. Give it what it needs as flags, and the server's address and an access token in KRIBBLE_URL and KRIBBLE_TOKEN. It publishes the first time and rebuilds every time after, so the same command works on every run:
kribble deploy "Engineering/SDK" --target ssh --target-name docs-host --subdir sdk --wait
--wait follows the build and exits with an error if it fails, so the job fails too. --json prints the result (with the site's URL) for later steps.
To keep a site in step with docs that live in a repository, push the docs and rebuild the site in the same job:
kribble push docs
kribble redeploy "Engineering/SDK" --wait
Which token to use:
Publish only is enough for kribble deploy, kribble redeploy, kribble sites and kribble targets.
kribble push also reads and writes notes, so a job that pushes needs Read + Write (MCP agents) or All scopes.
Either way, the person who created the token needs admin rights in the space. Access tokens and their scopes are explained in AI agents.
A GitHub Actions job, with the token saved as the repository secret KRIBBLE_TOKEN:
name: Docs
on:
push:
branches: [main]
paths: ["docs/**"]
jobs:
docs:
runs-on: ubuntu-latest
env:
KRIBBLE_URL: https://kribble.example.com
KRIBBLE_TOKEN: ${{ secrets.KRIBBLE_TOKEN }}
steps:
- uses: actions/checkout@v4
- run: |
curl -fsSL "$KRIBBLE_URL/install.sh" | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- run: kribble push docs
- run: kribble deploy "Engineering/SDK" --wait
Without the CLI, an HTTP request rebuilds the site of a whole space, with its settings:
curl -X POST "$KRIBBLE_URL/api/v1/spaces/$SPACE_ID/publish" \
-H "Authorization: Bearer $KRIBBLE_TOKEN"
This endpoint needs a token with the publish scope (Publish only or Publish + Import are enough). It only rebuilds a space that is published already; publish it once first. kribble ls --json shows a space's id.