0.1.0
Not yet published. There is no tag and no published image. This page describes what 0.1.0 will contain; the date and the pull command go in when it ships.
The first release. One vertical slice, thin in every layer: a model is parsed, validated, provisioned into PostgreSQL, and served as a REST API whose every query — read or write — is filtered by the caller’s role.
What you can build
An application is a directory of YAML. From roughly forty lines you get:
- A database schema, provisioned from your model and migrated additively.
- A REST API with cursor pagination, conditional writes and RFC 9457 error documents.
- An OpenAPI document generated per caller — entities and fields a token cannot reach are absent from it.
- Access control at three levels — entity, field and row — compiled into every query rather than applied afterwards.
- Lua flows, sandboxed, for the logic a declarative model cannot express.
- Business metrics you declare and never instrument.
- OpenTelemetry for traces, metrics and logs, over OTLP only.
See Your first application for the whole loop.
Worth knowing before you start
These are the decisions most likely to surprise you. Each is deliberate, and each has bitten somebody on a product that decided otherwise.
Money is never a number. decimal crosses every boundary as a string — in
JSON and in Lua. A binary float cannot represent 0.10. Sending 12.10 as a
JSON number is an error, not a rounding.
Writes require If-Match. PATCH and DELETE without one are refused with
428. Two people editing the same record is not a race condition, it is a
Tuesday, and last-write-wins loses one of the changes with no error at all.
There is no PUT. A role that cannot read every field cannot send a
complete representation, so the only honest full-replacement semantics would be
“and blank everything I cannot see”.
A hidden row and a missing row are both 404. Answering differently would
let anyone confirm an identifier by asking for it. The same reasoning makes a
field you cannot read report as “not a field of this entity” — identical to a
typo.
A flow runs as whoever called it. There is no runAs: and no way to
elevate. A flow that reports a total across rows the caller cannot itemise is
not expressible.
Metric labels are enum and boolean only. A label with a thousand values
is a thousand time series. The model does not load if you try.
Running it
Requires PostgreSQL 18 or later, and an OCI engine to run the image. The runtime is a single stateless binary; run it with a read-only root filesystem, because it writes nothing. The full list of what you have to provide is in Deployment, which is where it stays current — this page describes one version.
Two configuration values are worth setting deliberately before anything else —
RUNTIME_DATABASE_MAX_CONNECTIONS, whose real value is this times your replica
count, and terminationGracePeriodSeconds above 35, without which pods are
killed mid-drain. Deployment covers both.
Known limitations
Worth checking before you build something that needs one:
| Limitation | Detail |
|---|---|
| No web application layer | An application is an API only; no pages, forms or layout yet |
| No sorting, and no total count | Rows come back by creation time; keyset pagination has no count |
| No CORS | A browser client must be same-origin, behind a proxy |
No ?filter= | ?expand= exists; there is no way to ask for a subset of rows |
?expand= is forwards-only | reference associations only, and no reverse direction |
| One row per write | No bulk create or update |
referenceSet cannot be written | Junction tables are created; nothing fills them |
| Metrics are counters only | “Orders placed” works; “revenue booked” does not |
| A flow operates on one row | No collection, scheduled or write-triggered flows |
| Lua only | The WebAssembly engine — and so Python — is designed for, not built |
| Single tenant | One model, one database, per deployment |
| No rate limiting | A body limit and a request timeout exist; nothing bounds volume |
| No hard CPU bound on a script | Interpreted loops and memory are bounded; a long C call is not |
coroutine is absent from flows | A Lua hook is per-thread, so it would escape the instruction budget |
| The model is mounted, not pulled | The design is an OCI artifact fetched at start-up; today it is a directory |
Upgrading
Nothing to upgrade from. When there is, upgrade notes go here, and anything breaking is called out at the top of the page rather than in a table halfway down.