Hamolus
A headless data platform on Cloudflare Workers. You describe a collection as metadata; Hamolus creates the D1 table, serves a multi-tenant CRUD API for it, and generates the admin console, panels and AI tooling around it.
There is no application to fork. A Hamolus project is assembled from parts:
core/ the API Worker (thin seam over @hamolus/core)
console/ SolidJS admin app
panels/ generated data-driven app surfaces
mcp/ Model Context Protocol server for AI agents
site/ the public site (Astro, Next.js) reading the core over REST
seeds/ data generators
configs/ wrangler presets (staging, production, per land)
Quickstart
npm create hamolus@latest # asks twelve questions, then builds the project
or, if you would rather answer with flags — or already have the CLI:
pnpm add -g @hamolus/cli # or: npx @hamolus/cli
hamolus init # the same twelve questions
hamolus create acme # or skip them entirely
cd acme
pnpm install
pnpm dev # every part, in parallel
hamolus init and npm create hamolus@latest are the same command; every question has a
flag, and a question whose flag you pass is not asked. So a project is one line:
hamolus init acme --mode centralized --land acme --with-console --with-mcp --with-site nextjs
and hamolus create acme plus a few hamolus add calls is the same project, written out.
hamolus add console # admin app
hamolus add panel basic # a generated panel app
hamolus add mcp # MCP server for AI agents
hamolus add site blog # a public Astro site reading the core over REST
hamolus add seed basic # example content, so a new site has something to show
hamolus add configuration basic
Each flag adds that part and nothing else, so a project is one line:
npm create hamolus@latest acme --with-console --with-mcp --with-site nextjs
Example content
A new project has an empty database, so a new site renders an empty page — which reads as a broken site rather than an empty one. A seed is a self-cleaning Node script that fills the core with a few records, and the wizard offers it as question 12:
hamolus create acme --with-site astro # question 12 defaults to yes: a site wants content
hamolus create acme --with-console # and to no: a console is only empty
hamolus create acme --with-site astro --seed basic # or say so
hamolus create acme --with-site astro --no-seed # or decline it
Run it once the core is up, which is why it is not part of pnpm dev — a seed is a one-shot
script, and starting it before the core listens is a connection error:
pnpm dev
ADMIN_KEY=$(grep '^ADMIN_KEY=' core/.dev.vars | cut -d= -f2-) pnpm -F ./seeds/basic seed
Re-running it is safe: the script drops the collections and media it owns before recreating them, so it never duplicates records.
What the basic seed fills is the articles collection, because that is what the generated
site reads — the templates request articles by that exact name, so a prefixed collection
would leave the home page just as empty. Four articles come with it, one deliberately a
draft, so the site's status = published filter is exercised rather than assumed.
A seed has no --with- spelling, unlike the parts above. It writes to a core over HTTP, so
"a seed with no core" is a script that can never run — hamolus create acme --console --seed basic is refused rather than scaffolded into a directory that fails on its first fetch.
--console or --with-console
Every part flag has two spellings, and the difference is whether you get a core.
hamolus create acme --with-console # a core and a console
hamolus create acme --console # a console, and no core
--with- is the whole project: a core, its mode, its land and colony, core/.dev.vars with
a generated JWT_SECRET and ADMIN_KEY, and a deploy script. The bare flag is
hamolus add console applied to a new project, so you get only the console. It has no
core/, no secrets and no mode, and it talks to a core that already runs somewhere else —
at runtime, not compiled in, so the same build works against a preview or a production core
without being rebuilt. The console asks for the endpoint in its navbar; an MCP server reads
CORE_API_URL from mcp/.env; a site reads PUBLIC_HAMOLUS_ORIGIN from site/.env.
The bare spelling composes, so a workspace of parts is still one line:
hamolus create acme-console --console --mcp # no core
hamolus create acme --with-console --with-mcp # a core, and both
Mixing the two spellings is refused, because --console says there is no core and
--with-mcp says there is one. A panel is a page inside the console, so
--panel admin on its own builds fine and displays nothing — hamolus add panel warns, and
--with-panel admin is usually what you want.
hamolus list shows what a project contains and where each part came from.
The wizard writes a working JWT_SECRET and ADMIN_KEY into core/.dev.vars, so
pnpm dev runs without a second step. That file is git-ignored; anything you deploy wants
wrangler secret put instead.
Reaching a dev server from your phone
The seventh question is the LAN. y binds the core, the console, the MCP server and any
site to 0.0.0.0; a host name like mac.lan binds them there instead; n keeps everything
on 127.0.0.1. The answer is written into each part's dev script rather than left as a
flag to remember, so a project is either reachable as a whole or not at all. It is also
hamolus create acme --host 0.0.0.0 afterwards, and hamolus link-free either way.
Dev ports
The core listens on 8787 and the MCP server on 8788, so a root pnpm dev can start both
at once. Both read their port from the environment when you need a different one:
HAMOLUS_CORE_PORT=8797 HAMOLUS_MCP_PORT=8798 pnpm dev
A second checkout of the same project on one machine is a variable rather than a source
edit. This is a wrangler dev setting only — a deployed Worker is addressed by its URL
and has no port, so nothing here affects a deploy.
Core templates
The core is generated from a named template under templates/cores/:
hamolus create acme --core predefined
A collection or panel declared in core/src is frozen against the API — PUT/DELETE
answers 403 CODE_DEFINED_COLLECTION / 403 CODE_DEFINED_PANEL — while its records stay
fully editable. Anything not declared there stays editable everywhere.
Working against a local checkout
hamolus create acme --link ../hamolus # or: hamolus link ../hamolus
Linking rewrites the @hamolus/* dependencies to absolute link: paths so changes
in the checkout are picked up immediately. hamolus link --clear puts the registry
ranges back.
Modes
The core's CORE_MODE decides how land resolution and rewriting behave:
Pick one at creation time with --mode, or add a configuration per mode later.
Deploy
Each runtime part has a public, generated repository you can deploy to Cloudflare without touching the shell. The button forks the repo, provisions the D1/KV/R2 resources it declares, and wires Workers Builds for you:
These repositories are generated from packages/* by
scripts/export-deploy-repo.mjs, so a fork is always the current source — never
a stale snapshot. A panel is a page inside the console, so it deploys with it.
A button deploy does not set secrets — nothing secret goes through GitHub. After
the first deploy, put them on the Worker from the repository it forked into
(core/, mcp/ respectively):
# core
wrangler secret put JWT_SECRET
wrangler secret put ADMIN_KEY
The console is static assets and needs none of this. Nothing else is required — D1, KV and R2 are provisioned for you and documented in each repository's README.
The MCP worker needs no secret at all — but it does need one step a button cannot
do for you. It has exactly two plain vars in its own wrangler.jsonc, because neither
is confidential in the way a signing key is:
CORE_API_URL— set it to your core, including/api. The template shipshttp://localhost:8787/api, which is a placeholder, not a default.MCP_INSTANCE_ID— shipped empty on purpose. An instance id is a credential, so there is no guessable default to fall back on, and a worker that serves nothing until you say so is better than one that serves something to whoever deploys it first.
So a freshly forked MCP repo deploys and then refuses every request, saying it is not configured. That is the intended first state, not a broken build. To bring it up:
- Open the console against your core and create an instance under Environment → MCP.
- Set
MCP_INSTANCE_IDto the id it gives you, andCORE_API_URLto your core. - Issue a token under that instance and send it as
Authorization: Bearer <token>.
Callers authenticate with that per-user token, which is revocable one at a time — the
thing a shared MCP_BEARER_TOKEN could never do. See
the MCP reference.
Packages
packages/plugins/console/{todo,kanban} are example console plugins used by
hamolus add plugin.
Working in this repository
pnpm install
pnpm typecheck # all packages
pnpm build # all packages
pnpm check:markers # template token + theme-script drift gate (offline)
pnpm check:workspace-glob # workspace + glob regression gate
pnpm check:copyright # copyright/author notice on every published file
pnpm check:generated-app # generates a project, installs it, typechecks + builds it
pnpm check:code-definitions # offline gate for code-defined collections/panels
pnpm check:code-defined-core # the same contract over HTTP (needs a generated core)
pnpm check:panel-acl # live panel ACL gate (needs a running core)
pnpm check:mcp-instance-acl # live MCP instance/token ACL gate (needs a running core)
pnpm check:scope-colony-resolution
pnpm hamolus -- <args> runs the CLI from source.
Canonical CLI templates live in templates/ and are copied into
packages/cli/templates by prepack, so a published CLI ships them.
check:generated-app is the slow gate: it needs a warm pnpm store (it installs the
generated project's devDependencies) and a built console library
(pnpm -F @hamolus/console build:lib). check:markers is its fast counterpart —
it proves the template copy is faithful without installing anything.
Documentation
- Docs index
- Definitions — collections, fields, panels, config
- Collection definition — the schema of one table
- Field definition — one column, and all 19 types
- Panel definition — views, metrics and access
- Config definition — locales, uploads, theme
- Architecture
- Core API reference
- KV settings
- Console guide
- Panels
- MCP server
- Deploying
- Contributing — workflow, gates, compatibility, and how to report a security issue privately
- Writing guide — conventions for prose and code here
License
MIT — see LICENSE.