Minimal Core

The data layer, already written.

Bring the idea. Let Claude draw the screens. Ship a product in four weeks, not a year.

One binary over the database you already run. Every table, served as REST and MCP. Every role, decided on the table. Every call, on the record. The year of backend a product needs before it exists — finished before you begin. API Bay and Chat Studio were built this way. Now it is yours to build on.

Free tier, no card. Your database stays where it is; nothing is copied out of it.

database → tables → rest + mcp → roles → audit

One table, three calls

Acme’s employee table, read, refused, and read by an agent.

The organization acme, the project people, a Postgres database registered as acme. Every request below is lifted from the Book of Minimal and runs as printed against a deployment.

The table is the last segment of the path. fc picks columns, oy orders, ps and pg page. No route was written for this; none is written for the other ten relations either.

http
GET /minimal/api/rest/auto/v1/acme/people/pg/acme/employee?fc=id,full_name,department_id&oy=id.as&ps=5&pg=0 HTTP/1.1
Host: api.acme.example
LB-Access-Token: <your-access-token>
json · 200
[
  { "id": 1, "full_name": "Rafael Delacroix", "department_id": 11 },
  { "id": 2, "full_name": "Rafael Müller", "department_id": 7 },
  { "id": 3, "full_name": "Rafael Novak", "department_id": 8 },
  { "id": 4, "full_name": "Kwame Haddad", "department_id": 6 },
  { "id": 5, "full_name": "Rafael Haddad", "department_id": 6 }
]

Filters are column=operator.value — department_id=gt.6, full_name=like.Raf*. Page size and page number are mandatory, so no call ever reads a table whole by accident. Vol. I, Chapter 5 is the full query language.

The same path, a DELETE, sent with a token minted for hr-analyst. The table’s permission template lists hr-admin alone on delete. The refusal names the roles the caller held, not the rows it would have touched.

http
DELETE /minimal/api/rest/auto/v1/acme/people/pg/acme/employee?id=eq.5 HTTP/1.1
Host: api.acme.example
LB-Access-Token: <token · roles hr-analyst · key_type -r-->
json · 403
{
  "code": 403,
  "status": "Forbidden",
  "message": "insufficient permissions for this operation on this table, got [hr-analyst]"
}

Three gates, checked in order: a lock mask says whether anyone may delete here at all; the template says which roles may; a row scope, if set, says which rows. The token itself could not have helped — -r-- is read-only, and a token never carries more than its minter held. Vol. I, Chapter 6.

The same read as a tool call. auto_read_rows is one of 115 tools listed by tools/list; the arguments name the table the way the path did. The call is checked against mcp_read_roles on the same template, and recorded under the session the agent opened first.

http
POST /minimal/mcp/rpc/v1 HTTP/1.1
Host: mcp.acme.example
LB-Access-Token: <your-access-token>
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: auto_read_rows

{
  "jsonrpc": "2.0", "id": 2, "method": "tools/call",
  "params": {
    "name": "auto_read_rows",
    "arguments": { "type": "pg", "schema": "acme", "table": "employee", "page_size": 5, "page_number": 0 }
  }
}
json · 200
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      { "type": "text", "text": "[{\"id\":1,\"full_name\":\"Rafael Delacroix\",\"department_id\":11}, …]" }
    ]
  }
}

MCP invents no capability of its own: everything an agent can reach this way, a backend can reach over REST, refused for the same reasons. What MCP adds is the session — a persistent record of what the agent actually did. Vol. I, Chapter 7.

What it replaces

Every team that puts a database behind an API pays the same tax.

Write the routing. Write the auth middleware. Write the tenant check that runs before every query. Decide who may read which table and who may write to it. Write the same list, get, insert, update and delete handler for the eleventh table this month. None of it is specific to what your product does — a payroll table and an inventory table need exactly the same plumbing — and most teams write it twice a year for the rest of their careers.

Minimal Core is that tax removed. What is left to write is the one endpoint that computes a payroll total, not the fifty that move rows.

The argument in full: Vol. I, Chapter 1 — Why Minimal.

Two doors into every table.

Both open at once. Choosing between them is the first design decision you make for any piece of data.

Auto API — the routes that are just routes.

List, filter, insert, update, delete, on every table a project has indexed, with no authoring step. GET, POST, PUT, DELETE; column selection, ordering, grouping and paging as query parameters; JSON, CSV or YAML back. This is the door for the admin screen, the mobile backend, the fifty handlers that only move rows.

Custom API — the logic that is actually yours.

One YAML definition: a method, a path, a version, the roles allowed to call it, and an ordered waterfall of steps. A sql step is one statement with ?:name placeholders; a lua or lark step reshapes what the step before fetched. Mark a step omit and its raw rows never leave the server. Written in dev, copied to prod once staging has proven it.

The payroll total is a definition. The payroll table is not. Vol. II, Chapter 1 writes the first one end to end.

Who is asking

Identity arrives on five headers. Permission lives on the table.

Every request names an organization, a project, a space, a user and a comma-separated list of roles — as five headers, or as one access token that stands in for them. Minimal never defines what a role means. A role is a string; a table says which strings may do what.

Permission template

A named policy, assigned to as many tables as you like. Twelve role lists: four verbs across the REST, MCP and machine-to-machine channels. Change the template and every table on it changes. An empty list means nobody, not everybody.

Lock mask and visibility

A lock switches a verb off for everyone on a channel, whoever they are — a table nobody should delete from stays that way even for the role that could. A hidden table is not offered to a caller at all.

Row-level security

One column bound to one request header. Every read is filtered to rows where they match; an insert has the column set from the header whatever the body said. department ← X-Department turns one view into a different table per caller.

Access tokens

Minted with a name, a role subset, a key_type in create-read-update-delete order (crud, -r--) and an expiry. A token is an alias for an identity, never a bigger grant than its minter held.

Spaces

dev, staging, prod, or whatever split you want, under one project. Definitions and modules live in exactly one space until you copy them. The tables are shared; the logic is promoted.

The DDL surface

Register a database, read the live catalog, run a schema change as an ordered list of statements under an exclusive run lock, drop a database — each gated by its own role list on the organization or project.

What Minimal Core does not do. It does not authenticate anyone. The five headers are trusted as given, by design, because Minimal runs behind a boundary that has already verified a real person or service and sets those headers itself — a private network, or a gateway. Expose it to a network you do not fully trust and that boundary has to exist and be the only path in.

What stays yours. The identity provider, the gateway, third-party integrations, and any logic unrelated to your database. Vol. III builds the boundary once, concretely — Zitadel for identity, APISIX as the only public listener, Minimal on loopback behind it, one Postgres for all three — so you can copy it rather than design it.

What you run

One binary, one YAML file, one definition store.

Minimal reads a single configuration file at startup. Its own state — organizations, projects, templates, definitions — lives in a definition store you point it at: a Postgres or MariaDB database, with connection secrets sealed under an encryption key you hold. Your tenant databases are registered, read and written in place. Nothing is copied out of them.

Listeners

REST on one port; the MCP listener on its own, opened only if the mcp: block exists. TLS with a minimum version and cipher preferences; optional mutual TLS that can require and verify a client certificate. Bound to loopback in the reference deployment, reached only through the gateway.

Budgets

A 4 MiB cap on every request body, REST and MCP alike — a real memory bound, not a formality. Script steps get their own accounting: execution time, value size, nesting depth, pattern length, and a concurrency ceiling no single organization can take more than half of while another is running.

The record

Every call leaves an audit row: who, which route, which status, how long, from which address, under which token and session. Readable at three scopes — your own trail, your organization’s, the deployment’s. Traces export to any OpenTelemetry collector; a caller’s request is never blocked on telemetry.

The whole file, setting by setting, including the one default that deliberately is not safe: Vol. II, Chapter 9.

Built on it

Five products on this site. None of them has a data layer of its own.

Each one is an interface over the calls this page describes — a registered database, Auto API routes, a permission template per table, tokens, MCP tools, the audit trail. The layer under them is what Minimal Core licenses: the same calls, for the product you have in mind.

PostgREST and Hasura also generate an API from a schema. Minimal Core generates the API, serves the same operations as MCP tools, and permissions both channels per identity from one template on the table.

The ask

Three ways in. Two of them cost nothing.

Try it · $0

The hosted deployment at selectstar.wtf. Free tier, no card, no expiry. Volume I runs against it as printed; Chapter 2 has you reading rows before the end of the page.

Experience Now

Run it · $100 a year

The full licence on hardware you control, air-gapped if you need it. Every feature unlocked, plus $1 per 1,000 requests, prepaid and never expiring. Support is free; an on-call engineer is $150 an hour. Beta; indicative until final numbers publish on 1 Nov 2026.

See pricing

Be granted it · $0 for twelve months

Ostrich: ninety teams a year, the suite on their own hardware, $333,333 of credit against the licence. Pelican: qualifying non-profits, renewed on review, no cap. Both open on 1 Nov 2026.

Read the grant terms

Put it in front of a database this afternoon.

Open the hosted deployment in one tab and Volume I in another. If you would rather have it stood up against your own database, book a session and we do it with you.

Free tier, no card, nothing copied out of your database.