Skip to content

Blueprint Reference

Blueprints are TOML files that declaratively define your application. This page covers every section of a blueprint.

Every blueprint starts with a version and project metadata:

version = "0.1.0"
[project]
name = "My App"
version = "1.0.0"
description = "An internal tool"
[project.runtime]
min_version = "0.1.0"

Entities define your data model. The runtime creates database tables automatically.

[entity.Post]
fields = [
{ name = "id", type = "ULID", primary_key = true },
{ name = "title", type = "Text", required = true },
{ name = "body", type = "LongText", required = true },
{ name = "status", type = "Enum", values = ["draft", "published"], default = "draft" },
{ name = "authorId", type = "Ref", ref = "User.id", index = true },
{ name = "createdAt", type = "DateTime", default = "now" }
]
TypeDescription
ULIDPrimary key, auto-generated unique identifier
UUIDUUID identifier
TextShort text (VARCHAR)
LongTextLong text (TEXT)
EmailEmail address with validation
IntegerWhole numbers
FloatDecimal numbers
Booleantrue/false
DateTimeDate and time
DateDate only
JSONJSON data
EnumEnumerated values (use values = [...])
RefForeign key reference (use ref = "Entity.field")
OptionTypeDescription
primary_keybooleanMark as primary key
uniquebooleanEnforce uniqueness
indexbooleanCreate database index
requiredbooleanCannot be null
nullablebooleanExplicitly allow null
defaultanyDefault value ("now" for DateTime)
valuesstring[]Allowed values for Enum type
refstringForeign key target for Ref type
[entity.User.relations]
posts = { type = "hasMany", entity = "Post", foreign_key = "authorId" }
[entity.Post.relations]
author = { type = "belongsTo", entity = "User", foreign_key = "authorId" }

Relation types: hasMany, hasOne, belongsTo, manyToMany (requires through).

Define who can read, create, update, or delete records:

# Simple boolean
[entity.Post.access]
read = true
create = true
update = { authorId = "$currentUser.id" }
delete = { "$currentUser.role" = "admin" }

Use $currentUser to reference the logged-in user’s fields. Combine conditions with or/and:

[entity.Post.access]
read = { or = [{ status = "published" }, { authorId = "$currentUser.id" }] }

Field-level access:

[entity.User]
fields = [
{ name = "email", type = "Email", access = { read = true, write = { "$currentUser.role" = "admin" } } }
]

Pages bind URL paths to layouts and queries.

LayoutDescription
listTable view of query results
detailSingle record display
formCreate/update/delete forms
dashboardWidgets and summary cards
authAuthentication pages (sign-in)
[page."/posts"]
title = "All Posts"
auth = "none"
layout = "list"
[page."/posts".query.posts]
entity = "Post"
where = { status = "published" }
orderBy = { createdAt = "desc" }
limit = 20
include = ["author"]
[page."/posts/:id"]
title = "Post Detail"
auth = "none"
layout = "detail"
[page."/posts/:id".query.post]
entity = "Post"
where = { id = "$params.id" }
include = ["author"]
[page."/"]
title = "Dashboard"
auth = "required"
layout = "dashboard"
[page."/".query.recentPosts]
entity = "Post"
orderBy = { createdAt = "desc" }
limit = 5
[page."/".query.totalUsers]
entity = "User"
OptionDescription
entityEntity to query
whereFilter conditions (use $params.id for URL params)
orderBySort order ({ field = "asc" } or "desc")
limitMaximum number of results
offsetSkip N results
includeRelated entities to include

Forms handle record creation and updates.

[page."/posts/new"]
title = "New Post"
auth = "required"
layout = "form"
[page."/posts/new".form]
entity = "Post"
method = "create"
[[page."/posts/new".form.fields]]
name = "title"
type = "text"
label = "Title"
required = true
[[page."/posts/new".form.fields]]
name = "category"
type = "select"
label = "Category"
options = [
{ value = "general", label = "General" },
{ value = "finance", label = "Finance" }
]
[[page."/posts/new".form.fields]]
name = "body"
type = "textarea"
label = "Content"
rows = 10
required = true
[page."/posts/new".form.onSuccess]
redirect = "/posts/{id}"
message = "Post created!"

text, textarea, email, password, number, select, checkbox, radio, file, date, datetime

OptionDescription
labelDisplay label
placeholderPlaceholder text
requiredField is required
defaultDefault value
optionsChoices for select/radio (string array or { value, label } objects)
rowsNumber of rows for textarea
patternRegex validation pattern
error_messageCustom error message
min / maxNumber range constraints
acceptAccepted file types for file input

Detail pages can include an action bar with workflow triggers:

[page."/issues/:id"]
title = "Issue Detail"
layout = "detail"
[page."/issues/:id".actionBar]
statusField = "status"
[[page."/issues/:id".actionBar.actions]]
label = "Set Status"
workflow = "SetIssueStatus"
style = "primary"
[[page."/issues/:id".actionBar.actions]]
label = "Request Approval"
workflow = "RequestApprovalIfNeeded"
style = "secondary"

Customize layout rendering with named slots:

LayoutAvailable Slots
listlist.header, list.body, list.empty
detaildetail.main, detail.related
formform.form
dashboarddashboard.widgets
[ui]
render_mode = "server"
theme = "default"
view_transitions = true
[ui.tailwind]
primary_color = "#3B82F6"

Built-in themes: default, minimal, dashboard.

[auth]
providers = ["email"]
trustedOrigins = ["http://localhost:3000"]
[[auth.apiKeys]]
name = "my-agent"
keyEnv = "MY_API_KEY"
[notifications]
default = "console"
[[notifications.adapters]]
name = "console"
type = "console"
[[notifications.adapters]]
name = "slack_ops"
type = "slack"
[notifications.adapters.config]
botTokenEnv = "SLACK_BOT_TOKEN"
defaultChannel = "#ops"

Adapter types: console, slack, email.

Use the framework stories package to test your blueprints:

Terminal window
pnpm --filter @zebric/framework-stories test

See the examples/ directory for complete blueprint files.