Fibel publishes Markdown and host-rendered application pages in one documentation shell with native language routes, server-side search, raw Markdown sources, and a cookie-based light/dark theme.
It is built for product and developer documentation that should stay readable in the repository, work well as a website, and remain easy for tools such as LLMs to consume.
Fibel targets Bun.
For an app project:
bun add @k2b/fibel
bunx --bun @k2b/fibel init
bunx --bun @k2b/fibel dev --port 5173Open http://localhost:5173. The root path redirects to the configured default locale.
fibel dev watches the config, docs, and assets. After a successful rebuild, connected browser tabs reload automatically. Use --no-watch or --no-reload when a plain local server is enough.
Fibel moved from @valentinkolb/fibel to @k2b/fibel in v0.2.0. Replace root imports with @k2b/fibel, plugin imports with @k2b/fibel/plugins, and CLI commands with bunx --bun @k2b/fibel. The previous package is deprecated and receives no further releases.
Fibel ships a Codex agent skill for documentation work. Install it in agent environments that should understand Fibel projects:
bunx skills add k2b-dev/fibelThe skill tells agents how to configure Fibel, write Markdown pages, use raw .md routes, extend plugins, mount the Fetch app, and verify changes.
When working from this repository:
bun install
bun run src/cli.ts init
bun run src/cli.ts dev --port 5173- Documentation pages from
docs/<locale>/**/*.md - Framework-neutral custom pages with searchable Markdown context
- Optional Solid SSR and island integration through a host-owned
@k2b/ssrbuild - A standalone Web-standard
fetchapp - Server-rendered HTML with page metadata and canonical URLs
- Server-side search with an interactive spotlight dialog
- Keyboard shortcuts for search with
/andMod+K - Stable raw Markdown routes with
.mdand.markdown - Native language routing and language switching
- Light and dark mode without client-side theme flicker
- Static assets from an
assets/directory - SEO routes for
robots.txt,sitemap.xml,favicon.ico, andfavicon.svg - Language alternates, social cards, and structured data on every page
llms.txtandllms-full.txtroutes for language models- A Tailwind-based default theme
- A small plugin API for replacing or extending built-in behavior
.
|-- fibel.config.ts
|-- docs/
| |-- en/
| | |-- index.md
| | `-- configuration.md
| `-- de/
| |-- index.md
| `-- configuration.md
`-- assets/
`-- logo.svgEach locale has its own folder below docs/. A file at docs/en/configuration.md is served as /en/configuration. The same source is also available as /en/configuration.md and /en/configuration.markdown.
Create fibel.config.ts at the project root:
import { defineFibel } from "@k2b/fibel";
export default defineFibel({
title: "Product Docs",
description: "Documentation for Product.",
siteUrl: "https://docs.example.com",
locales: [
{ code: "en", label: "English" },
{ code: "de", label: "Deutsch" },
],
defaultLocale: "en",
routing: {
basePath: "/docs",
internalPath: "/_fibel",
assetsPath: "/assets",
},
theme: {
defaultMode: "light",
cookieName: "fibel_theme",
},
headerLinks: [
{ label: "Guide", value: "/runtime" },
{ label: "Plugins", value: "/plugins" },
],
footerLinks: [
{ label: "Imprint", value: "/imprint" },
{ label: "GitHub", value: "https://github.com/example/project" },
],
});basePath is the public mount path. With the config above, pages live under /docs, the search endpoint lives under /docs/_fibel/search, and assets live under /docs/assets/....
headerLinks fills the navigation next to the site title. Local values are resolved against the current locale, so /runtime points to /en/runtime on an English page and to /de/runtime on a German one. A link is marked as active when its value matches the slug of the current page. The header navigation is empty when headerLinks is not set.
footerLinks works the same way. Both lists take external URLs as written and resolve local paths against the current locale.
For a shared header across several Fibel instances, use the structured header config. Link functions receive the current locale, while activeWhen matches the instance prefix:
export default defineFibel({
title: "Cloud UI",
routing: { basePath: "/ui" },
header: {
title: "Cloud",
homeHref: ({ locale }) => `/${locale}`,
links: [
{
label: "Docs",
href: ({ locale }) => `/docs/${locale}`,
activeWhen: "/docs",
},
{
label: "UI",
href: ({ locale }) => `/ui/${locale}`,
activeWhen: "/ui",
},
],
searchLabel: "Search Cloud UI",
},
});renderFibelHeader() from @k2b/fibel/layout exposes the same markup for external pages. layoutPlugin({ header: false }) removes only the built-in header when an outer shell already provides it.
Fibel reads Markdown files from the configured content directory. The first # heading is treated as the page title and is rendered once by the layout. Headings from ## to #### receive stable IDs and copy-link buttons.
---
title: Configuration
navTitle: Configuration
section: Start
order: 20
description: Configure content, routing, locales, theme, footer links, and plugins.
tags: [config, routing]
updated: 2026-06-09
---
# Configuration
Use `fibel.config.ts` to describe the documentation site.Supported frontmatter fields:
title: Page title used for the heading, metadata, and search.navTitle: Short title used in navigation.section: Sidebar section label.order: Numeric sort order inside a locale and section.description: SEO description and page summary.hidden: Remove the page from navigation, pagination, site search,llms.txt, and the sitemap, and render it withnoindex. The page stays reachable at its URL.tags: List of tags rendered as page chips.updated: Date string rendered as a page chip and used asarticle:modified_time.image: Social preview image for this page, overridingseo.ogImage.
Place files in the configured assets directory. Fibel serves them below the configured assets route.

[Download the PDF](/assets/product-brief.pdf)If the app is mounted under basePath, link to assets through that public route.
pages adds server-rendered application output to the normal Fibel shell. Optional Markdown context remains the source for search, raw .md routes, llms.txt, and the documentation assistant.
export default defineFibel({
title: "Cloud UI",
pages: [
{
path: "/panel-header",
title: "PanelHeader",
description: "A consistent heading and action area.",
context: {
default: panelHeaderMarkdown,
de: panelHeaderMarkdownDe,
},
render: ({ context }) =>
`<section>${context.html}</section>`,
},
],
});Use solidPage() from @k2b/fibel/solid when the body contains Solid server components or @k2b/ssr islands. The host supplies its existing html() renderer and remains responsible for the single SSR plugin, /_ssr route, and production build. The custom pages guide includes complete examples for Solid and multiple Fibel instances.
Set siteUrl so canonical URLs, language alternates, and sitemap entries become absolute. Fibel then adds hreflang alternates for every translation of a page plus an x-default, so search engines treat the language versions as one page instead of competitors.
export default defineFibel({
title: "Product Docs",
siteUrl: "https://docs.example.com",
seo: {
ogImage: "/assets/social.png",
twitterSite: "@example",
disallow: ["/en/internal"],
},
});Pages marked hidden are left out of the sitemap and rendered with noindex. Social cards use seo.ogImage unless a page sets image in its frontmatter.
Every indexable page also carries JSON-LD with a TechArticle. Content pages add a BreadcrumbList built from the sidebar section; locale index pages add a WebSite entry instead.
Fibel publishes an llms.txt index next to the raw Markdown routes.
/llms.txt index for the default locale
/en/llms.txt index for a specific locale
/llms-full.txt every page of the default locale in one file
/en/llms-full.txt every page of a locale in one fileThe index lists each page grouped by sidebar section and links to the raw .md route with its description. Hidden pages are excluded.
Fibel builds a search index from page title, description, section, and Markdown body. The default theme includes a spotlight search dialog that opens with / or Mod+K.
Search is server-side by default. The browser sends the query to the internal search endpoint and renders the result list without a full page reload.
The default theme stores the selected mode in a cookie. The server reads the cookie and renders the initial HTML with the correct root class:
<html class="dark" data-theme="dark" style="color-scheme:dark">This avoids a visible switch from one theme to the other after hydration.
Fibel exposes a Web-standard Fetch app. Hosts do not need a specific framework.
import { createFibelApp } from "@k2b/fibel";
import config from "./fibel.config";
const fibel = await createFibelApp(config);
export default {
fetch: fibel.fetch,
};For a larger app, mount fibel.fetch below the same public route configured as routing.basePath.
Several Fibel apps can be mounted in one process. Each instance then has its own navigation, search index, assistant context, and discovery routes while sharing the same header config, theme cookie, provider, rate limiters, and deployment.
The repository includes a Docker image for hosting the default Fibel documentation.
docker build -t fibel-docs .
docker run --rm -p 3000:3000 fibel-docsThe image uses a pinned Bun multi-stage build. Development dependencies are installed only in the build stage, where typecheck, tests, and fibel build run. The runtime stage uses production dependencies and starts the generated server as the non-root bun user.
Tagged releases publish the same image to GitHub Container Registry:
docker run --rm -p 3000:3000 ghcr.io/k2b-dev/fibel:latest
docker run --rm -p 3000:3000 ghcr.io/k2b-dev/fibel:v0.4.0Plugins can replace services, validate content, add routes, or derive metadata from the loaded pages.
import { defineFibel, defaultPlugins, type FibelPlugin } from "@k2b/fibel";
function requireTagsPlugin(): FibelPlugin {
return {
name: "require-tags",
afterContent(context) {
for (const page of context.pages) {
if (page.meta.hidden) continue;
if (!page.meta.tags.length) {
throw new Error(`${page.sourcePath} is missing frontmatter tags.`);
}
}
},
};
}
export default defineFibel({
title: "Product Docs",
plugins: [...defaultPlugins(), requireTagsPlugin()],
});The built-in plugin set includes Markdown rendering, theme handling, i18n checks, SEO metadata and routes, llms.txt routes, asset routes, search, powered-by attribution, and layout rendering. Individual plugins are exported from @k2b/fibel/plugins.
The optional assistant plugin uses @k2b/nessi to answer from visible documentation pages. Its default @k2b/sync/browser rate limiters and sessions live in the Fibel process, so a single-server deployment needs no Redis or database.
import { assistantPlugin, providerFromEnv } from "@k2b/fibel/plugins";
assistantPlugin({
provider: providerFromEnv(),
systemPrompt: "Help readers configure Product. Keep answers concise.",
});FIBEL_AI_PROVIDER=openrouter
FIBEL_AI_MODEL=provider/model-name
OPENROUTER_API_KEY=...The defaults limit each session to 5 requests per minute, the process to 100 requests per day, each response to 3 agent turns and 600 output tokens, and concurrent generations to 2. In-memory limits reset on restart and are separate per replica. Multiple replicas can inject Redis-backed limiters from @k2b/sync plus a shared Nessi session store. Configure a provider-account spending cap as the final financial limit.
bunx --bun @k2b/fibel init
bunx --bun @k2b/fibel dev --port 5173 --config fibel.config.ts
bunx --bun @k2b/fibel build --config fibel.config.tsfibel dev rebuilds on local documentation changes and reloads connected browser tabs after the rebuild succeeds. Disable either behavior with --no-watch or --no-reload. fibel serve is an alias for fibel dev and starts the same watching development server.
fibel build writes the deployable runtime. Serve it with bun dist/server.ts, which listens on PORT and defaults to 3000.
Repository scripts:
bun run dev
bun run build
bun run start
bun test
bun run typecheckfibel build writes a deployable runtime into dist/ and copies generated Fibel assets into dist/.fibel.
Publishing is handled by GitHub Actions through npm trusted publishing. Push a version tag to publish that version:
git tag v0.4.0
git push origin v0.4.0The workflow runs typecheck, tests, build, package-content checks, sets package.json to the tag version, and publishes with provenance:
npm publish --provenance --access publicNo npm token is required. The package is connected to .github/workflows/publish.yml through npm trusted publishing.
bun install
bun run typecheck
bun test
bun run buildThe repository includes example documentation in docs/en and docs/de. Use it as both the project documentation and a visual test case for the default theme.