New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more β†’
Get Started

koguma

Package Overview
Dependencies
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

koguma

🐻 A little CMS with big heart β€” schema-driven, runs on Cloudflare's free tier

latest
Source
npmnpm
Version
2.3.10
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
Β 
Created
Source

Koguma

Koguma

A little CMS with big heart β€” schema-driven content management that runs entirely on Cloudflare's free tier.

Koguma gives you a headless CMS with a beautiful admin dashboard, all powered by Cloudflare Workers + D1 + R2. Define your content types in code, and Koguma handles the rest β€” API routes, admin UI, media storage, and a file-based content/ directory for version-controlled content.

Quickstart

1. Install

bun add koguma
# or
npm install koguma

2. Define your content

Create a site.config.ts in your project root:

import { defineConfig, contentType, field } from 'koguma';

export default defineConfig({
  siteName: 'My Site',
  contentTypes: [
    contentType({
      id: 'post',
      name: 'Blog Post',
      displayField: 'title',
      fields: {
        title: field.text('Title').required(),
        slug: field.text('Slug').required(),
        body: field.markdown('Body'),
        heroImage: field.image('Hero Image'),
        published: field.boolean('Published').default(false),
        date: field.date('Published Date')
      }
    })
  ]
});

3. Create your worker

Create a worker.ts:

import { createWorker } from 'koguma/worker';
import config from './site.config';

export default createWorker(config);

4. Configure

Create koguma.toml:

name = "my-site"
database_id = ""    # filled by `koguma init`

That's it β€” two lines. Everything else is derived by convention:

  • D1 database β†’ my-site-db
  • R2 bucket β†’ my-site-media
  • Worker name β†’ my-site

5. Deploy

koguma init    # Scaffold project, create D1 + R2 on Cloudflare
koguma push    # Build + deploy + sync content

Your admin dashboard is at /admin. Your API is at /api/content/:type.

Content Schema

Field Types

FieldUsageStored As
field.text(label)Titles, slugs, URLsTEXT
field.longText(label)Descriptions, biosTEXT
field.markdown(label)Rich formatted contentTEXT (markdown string)
field.number(label)Counts, ordernumber in JSON
field.boolean(label)Togglesboolean in JSON
field.date(label)Timestampsstring (ISO 8601)
field.select(label, {options})Dropdownsstring
field.url(label)URLsstring
field.email(label)Email addressesstring
field.phone(label)Phone numbersstring
field.color(label)Hex coloursstring
field.youtube(label)YouTube video IDsstring
field.instagram(label)Instagram handlesstring
field.image(label)Image from R2 mediastring (asset ID)
field.images(label)Array of imagesstring[] (asset IDs)
field.ref(typeId, label)Link to another entrystring (entry ID)
field.refs(typeId, label)Array of entry linksstring[] (entry IDs)

All fields stored inside a JSON data blob in the entries table β€” no per-field columns, no migrations.

Content Type Options

contentType({
  id: "page",
  name: "Page",
  displayField: "title",
  singleton: true,           // Only one entry allowed (e.g. site settings)
  fields: { ... },
});

CLI Reference

All commands auto-detect your project root by looking for koguma.toml.

CommandDescription
koguma initInteractive project setup β€” scaffold, create D1 + R2, set secret
koguma devAuto-sync content/ β†’ local D1, generate types, start dev server
koguma pushBuild + deploy + sync content to remote
koguma pullDownload remote content + media β†’ local content/ files
koguma gen-typesGenerate koguma.d.ts typed interfaces
koguma tidyValidate content/ against config, sync dirs, check fields

Content Directory

Koguma uses a content/ directory for file-based content that lives in your git repo:

content/
β”œβ”€β”€ post/                          # collection β€” one file per entry
β”‚   β”œβ”€β”€ hello-world.md             # slug = "hello-world"
β”‚   β”œβ”€β”€ our-mission.md
β”‚   └── our-mission.heroBody.md    # sibling markdown field
β”œβ”€β”€ landingPage/                   # singleton β€” always index.md
β”‚   β”œβ”€β”€ index.md                   # slug = content type ID
β”‚   β”œβ”€β”€ heroBody.md                # sibling markdown field
β”‚   └── ctaBody.md                 # sibling markdown field
└── media/                         # local images (synced to R2)
    └── hero-banner.jpg

File Format

Every content file uses YAML frontmatter + markdown body:

---
title: Our Mission
slug: our-mission
heroImage: media-hero-banner
published: true
date: 2026-03-01
---

This body content maps to the **first** `field.markdown()` in the content type.

Singletons vs Collections

  • Collections (default): Each entry is a file named {slug}.md
  • Singletons (singleton: true): One entry, always index.md. The slug is auto-set to the content type ID.

Sibling Markdown Fields

When a content type has multiple field.markdown() fields, the first one maps to the file body. Additional markdown fields are stored as sibling files (pure markdown, no frontmatter):

CollectionsSingletons
Main file{slug}.mdindex.md
Sibling pattern{slug}.{fieldId}.md{fieldId}.md

Example singleton with 3 markdown fields:

content/landingPage/
β”œβ”€β”€ index.md           # frontmatter + 1st markdown field body
β”œβ”€β”€ heroBody.md        # 2nd markdown field (pure markdown)
└── ctaBody.md         # 3rd markdown field (pure markdown)

Git is your version history. No custom versioning table needed.

API Routes

Public (no auth)

MethodRouteDescription
GET/api/content/:typeList all entries of a content type
GET/api/content/:type/:idGet a single entry (with resolved refs)
GET/api/media/:keyServe a media file from R2

Admin (auth required)

MethodRouteDescription
POST/api/auth/loginLogin with { password }
POST/api/auth/logoutClear session
GET/api/auth/meCheck authentication
GET/api/admin/schemaContent type schemas (for admin UI)
GET/api/admin/:typeList entries
GET/api/admin/:type/:idGet entry
POST/api/admin/:typeCreate entry
PUT/api/admin/:type/:idUpdate entry
DELETE/api/admin/:type/:idDelete entry
GET/api/admin/mediaList media assets
POST/api/admin/mediaUpload media (multipart form)
DELETE/api/admin/media/:idDelete media

References and images are automatically resolved to nested objects in the public API (up to 2 levels deep).

Package Exports

import { defineConfig, contentType, group, field, type Infer } from 'koguma';
import { createWorker } from 'koguma/worker';
import { createClient } from 'koguma/client';
import { Markdown, useEntry, useEntries } from 'koguma/react';
import type { KogumaAsset, EntryReference } from 'koguma/types';

Local Development

# Create .dev.vars with your local admin password
echo "KOGUMA_SECRET=your-password" > .dev.vars

# Start dev server (auto-syncs content/, generates types)
koguma dev

# Admin dashboard is at http://localhost:8787/admin
# API is at http://localhost:8787/api/content/:type

Architecture

Your Project
β”œβ”€β”€ site.config.ts     ← Content type definitions
β”œβ”€β”€ worker.ts          ← Entry point (imports koguma/worker)
β”œβ”€β”€ koguma.toml        ← Project config (name + database_id)
β”œβ”€β”€ .dev.vars          ← Local secrets (KOGUMA_SECRET)
└── content/           ← Version-controlled content files
    β”œβ”€β”€ post/
    β”‚   └── hello-world.md
    └── siteSettings/
        └── index.md

Koguma (this package)
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ config/        ← Schema definitions (defineConfig, field types)
β”‚   β”œβ”€β”€ api/           ← Hono router (CRUD + media + auth)
β”‚   β”œβ”€β”€ db/            ← D1 queries (JSON document store)
β”‚   β”œβ”€β”€ auth/          ← HMAC-signed cookie sessions
β”‚   β”œβ”€β”€ media/         ← R2 upload/serve/delete
β”‚   β”œβ”€β”€ admin/         ← Dashboard HTML shell + JS/CSS bundle
β”‚   β”œβ”€β”€ client/        ← Fetch client for consuming the API
β”‚   └── react/         ← React hooks + <Markdown> component
β”œβ”€β”€ admin/             ← Vite + React admin dashboard source
└── cli/               ← CLI commands (init, dev, push, pull, gen-types, tidy)

License

MIT

Keywords

cms

FAQs

Package last updated on 09 Mar 2026

Related posts