# forinda-cms
**Your whole site is one file you can read.** Content types, pages, logic,
access, integrations — declared, not scattered across a plugin directory and a
database nobody wants to open.
Edit it three ways, and they are the same edit:
- **In the admin**, as forms — no YAML in sight.
- **In files**, with `fcms` — review it in a pull request like any other change.
- **With an AI assistant**, which proposes a change you approve or reject.
Same validation, same history, same undo, whichever door the change came
through.
```yaml
content:
- key: booking
label: Booking
fields:
- { name: name, type: text, required: true }
- { name: when, type: datetime }
- { name: state, type: state, values: [pending, confirmed, done] }
```
📖 **[Building a site](GUIDE.md)** — from nothing to a published site.
🔧 **[How it works](ARCHITECTURE.md)** — the data model, what `filterable`
compiles to, and where to look when something is wrong.
Longer tour and reference: <https://fcms.kickjs.app>
## Install
Node 22 or newer. Nothing else.
```bash
npx @forinda/fcms-cli init my-site && cd my-site
npm install
npm run dev
```
`init` writes a site you can already run — valid, canonical, and accepted by
every other command without being fixed first. To serve it for real:
```bash
OWNER_EMAIL=you@example.com OWNER_PASSWORD=a-long-enough-password npm start
```
That is the whole install. **Postgres runs inside the process** — the real
thing, compiled to WebAssembly — and keeps its data in `./.fcms`. No database to
obtain, no container, no connection string, and a backup is `cp -r`.
It migrates and provisions itself on boot, so the first run lands on a working
site at <http://localhost:8080>.
The embedded database belongs to one process at a time, and a second one is
refused rather than allowed to corrupt it. Already have a Postgres, or
outgrown one process? Same command, one variable:
```bash
DATABASE_URL=postgres://… npx @forinda/fcms-core
```
Same schema and same migrations either way, so moving between them is
`pg_dump` and nothing else. There is a Docker Compose file too, in
[`install/`](install/README.md).
## The CLI
```bash
npm install -g @forinda/fcms-cli
fcms dev # preview a site directory locally, no server needed
fcms plan # what publishing would change
fcms apply # publish it
```
`plan` and `apply` show you the change before making it, and refuse anything
destructive unless you say `--yes`.
## For an agent
The same doors, over MCP. Point any MCP client at a linked site directory:
```json
{
"mcpServers": {
"fcms": { "command": "npx", "args": ["-y", "@forinda/fcms-mcp"] }
}
}
```
It reads the credentials `fcms login` already wrote, and every change it makes
goes through the same validate → diff → classify → gate path as the admin and
the CLI, with an inverse recorded. An agent cannot reach the database, and it
cannot make a change a person cannot see or undo.
## What works today
The spec and its language, the renderer, the admin with its visual canvas, the
CLI, the MCP server for agents, and the install. On top of those, a business
that takes bookings has the whole path: availability by appointment or by date
range, distance, multi-step booking journeys, deposits, automations that confirm
and notify, and visitors who see their own bookings and nobody else's.
Being found is part of that and needs no setup: titles, canonicals, Open Graph,
`sitemap.xml` with a `lastmod` per page, `llms.txt`, JSON-LD from the content
type's own shape, and an automatic 301 when a page moves. Details in
[GUIDE.md](GUIDE.md#7-be-found).
Worth knowing before you rely on it:
- The M-Pesa and messaging providers are written against documented APIs but
have **never been run against a live account**. The product says so where they
are used; payment at the counter needs no account at all.
- Plugins have a contract (`@forinda-cms/plugin`) but no marketplace. Installing
one is a dependency and a deploy, reviewed — not a button.
- There is no hosted service. Self-host it, or run it for your clients.
## Developing
```bash
pnpm install
pnpm dev # the engine, with HMR
pnpm verify # typecheck, lint, format, schema, every test — exactly what CI runs
```
The database suites skip themselves when `DATABASE_URL` is unset, so this stays
runnable on a laptop with no Postgres. `pnpm --filter @forinda-cms/engine seed`
loads the reference site in `examples/salon`.
## Releases
One calendar-versioned tag publishes the image, the server package and the CLI,
all carrying the same number. [`RELEASE.md`](RELEASE.md) has the detail.
## Contributing
Issues and pull requests are welcome — [`CONTRIBUTING.md`](CONTRIBUTING.md) has
the workflow, and [`.agents/AGENTS.md`](.agents/AGENTS.md) the framework
conventions. Open an issue before a large change; it saves you writing a patch
the design cannot take.
## Licence
**AGPL-3.0-or-later** for the product — the engine, the admin, the renderer and
the `fcms` CLI. Self-host it, modify it, run it for clients. The one obligation:
if you run a *modified* version that other people reach over a network, publish
your changes.
**Apache-2.0** for the interop surface — `@forinda-cms/spec`,
`@forinda-cms/lang`, `@forinda-cms/sdk` and `@forinda-cms/plugin`. The file
format, the client and the plugin API are meant to be copied.
Your specs, content, themes and plugins are yours; nothing here claims them.
A commercial licence is available for anyone the AGPL does not suit —
[`LICENSING.md`](LICENSING.md) has the detail, including what the AGPL actually
asks of you (for most people: nothing).