Kobato (こばと。)
_"A little bird carrying hope, one letter at a time."_
Kobato is a self-hosted blog CMS built by Yufan Sheng
— the engine behind 且听书吟. It runs on React Router 8 (SSR), Hono, and oRPC,
with a built-in /admin console for everything.
Content is stored as Lexical editor state and authored through the built-in inkling editor; saved server-side HTML projections power SSR, feeds, and search.
This repository is the complete product: public site, admin SPA, API, SSR renderer, install gate, and database migrations. The whole deployment is one process — content lives in an embedded SQLite database, analytics in an embedded DuckDB sidecar. There is no database server to run, back up, or upgrade.
Contributors: start at AGENTS.md — it maps the folder-scoped
docs covering the import boundaries, the four-layer src/server/ graph, the
install contract, and the API permission matrix.
Features
- Posts, pages, categories, tags, and comments — all managed in a built-in
/adminconsole - Lexical content model with the built-in inkling editor
- Per-section settings (general, SEO, assets, comments, navigation, and more)
- First-party analytics with optional GeoIP enrichment
- Optional S3-compatible object storage for media
Requirements
- Node.js 26+ for development (the SEA binary deployment needs no runtime);
pnpm run sea:buildrequires Node.js 26
Quick start
Install dependencies and start the dev server:
pnpm install
pnpm run dev
On first boot a kobato.config.json is created in the repo root
(gitignored) — fill in the two secrets (see Configuration
for the file shape). The database files create themselves under
storage.data (./data by default):
{
"security": {
"sessionSecret": "", // openssl rand -hex 32
"encryptionKey": "", // openssl rand -hex 32
},
}
Open /admin/setup and enter the setup token printed in the console to
create the admin account. Settings are seeded automatically.
Configuration
Most settings are managed in the admin dashboard. Infrastructure
configuration (database, secrets, data paths) lives in
kobato.config.json — always present, auto-created with defaults when
missing:
--config/-c(SEA binary only)/kobato.config.json ./kobato.config.json~/.config/kobato.config.json
| Variable | Config path | Description |
| ---------------------------- | --------------------------- | ----------------------------------------------------------------------------- |
| storage__data | storage.data | Root data directory for the databases, fonts, uploads, and MaxMind DB |
| storage__database | storage.database | SQLite content database file, default /kobato.db |
| storage__analyticsDatabase | storage.analyticsDatabase | DuckDB analytics sidecar file, default /analytics.duckdb |
| security__sessionSecret | security.sessionSecret | HMAC secret for cookies. Generate with openssl rand -hex 32 |
| security__encryptionKey | security.encryptionKey | AES-256-GCM key for encrypting secrets in the database |
| security__trustedProxies | security.trustedProxies | Comma-separated proxy IPs / IPv4 CIDRs trusted to vouch for forwarded headers |
| server__host | server.host | Listen address, default 0.0.0.0 |
| server__port | server.port | Listen port, default 4321 |
| storage__defaultFont | storage.defaultFont | Optional fallback font file copied into /fonts |
| server__loggingLevel | server.loggingLevel | debug / info / warn / error / silent |
See kobato.config.example.json for the file shape.
Testing
The suite is fully self-contained — integration tests run against a shared in-memory SQLite database (temp files only for file-backed flows) and temp-file DuckDB sidecars, so no Docker or services are needed:
pnpm run test
For fast local feedback, run unit tests and snapshot tests only:
pnpm run test:unit
pnpm run test:snaps
Deployment
Every release ships a self-contained single executable — no Node.js
runtime, no node_modules, no database server. The server bundle, client
assets, and database migrations are embedded in the binary; the native
packages (sharp, canvas, DuckDB) are extracted to a cache directory on
first run. Targets: linux-x64, linux-arm64 (glibc), darwin-arm64,
win32-x64, and win32-arm64.
Download the archive for your platform (e.g. kobato-linux-x64.tar.gz)
and its .sha256 sidecar from the latest release, verify,
extract, and install:
sha256sum -c kobato-linux-x64.tar.gz.sha256
tar -xzf kobato-linux-x64.tar.gz
install -m 0755 kobato-linux-x64 /usr/local/bin/kobato
kobato --version # prints the baked-in version
Configure it through kobato.config.json (see Configuration):
on first boot the binary creates it next to itself with defaults — edit the
file, or pass __-style environment variables once and let them be written
into the file. storage.data defaults to ./data relative to the working
directory, so set it explicitly for a system install. The natives cache
lands in $XDG_CACHE_HOME/kobato (override with KOBATO_CACHE_DIR).
Database migrations run automatically at boot; on first boot, open
/admin/setup. The admin console's backup job packs both database files
(content + the analytics sidecar) into a single .tar.gz archive; legacy
content-only backups stay restorable.
A minimal systemd unit:
[Unit]
Description=Kobato blog CMS
After=network-online.target
[Service]
Type=simple
Generate real secrets with e.g. openssl rand -hex 32 — the schema
requires both ≥ 32 chars and encryptionKey ≥ 10 distinct characters.
Environment=security__sessionSecret=change-me
Environment=security__encryptionKey=change-me
Environment=storage__data=/var/lib/kobato
ExecStart=/usr/local/bin/kobato
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
The binary can update itself: in the admin console, open the version
dialog → 检查更新 → 立即更新. It downloads the release asset for the current
platform, verifies the sha256, swaps the executable in place (the previous
one is kept as kobato.bak for manual rollback), and restarts.
Scripts
pnpm run dev # development server
pnpm run build # production build
pnpm run test # run tests
pnpm run test:unit # run unit tests only
pnpm run test:snaps # run snapshot tests only
pnpm run fmt # formatting
pnpm run lint # lint
pnpm run type # TypeScript check
pnpm run db:gen # generate Drizzle migrations
Design assets
kobato.sketch is a Sketch template for the kobato favicon, logo, and branding assets.