Docs

The data and the api

Couchbase

Couchbase is somebody else's software, so the component says which image runs it and which driver speaks to it. It declares no code of ours:

kind = "online-service"
drivers.couchbase = { kind = "registry", package = "bluetext-driver-couchbase", version = "0.3" }

[interfaces.data]
kind                        = "other"
driver                      = "couchbase"
serves-api-configured-state = true

[interfaces.data.profiles.data]
credential-bundle = "admin"

Three lines are worth reading closely.

drivers.couchbase names the crate that speaks to it. A consumer's client types and methods come from that crate, so the driver is the contract: model code calling this link is writing Couchbase calls. The driver sits on the interface and cannot be changed by a variant, because that would change the API the model compiled against. That is why the component is called couchbase and not store — nothing else can ever be behind the name.

profiles.data is a named way of consuming the interface, and credential-bundle = "admin" says which credentials that way needs. A second consumer wanting weaker rights gets a second profile rather than sharing this one.

serves-api-configured-state says the server holds state that an api call has to establish before it is usable. For Couchbase that is the bucket, declared in a separate file:

[pre-consumer-apply]
buckets = [{ name = "todos", ram-mb = 256 }]

The deploy applies this before any consumer of the interface starts, so nothing races an empty server. Separately, the variant carries a [bootstrap] block naming a driver function that runs once against a fresh container — for Couchbase, creating the administrator that the bucket call then authenticates as.

The model

Business logic lives in parts, which belong to the system rather than to any component. A part declares what it reaches and which other parts it builds on:

# src/models/parts/todos-core.toml
links.store = "couchbase/data"
parts       = []

Its Rust lives beside it in src/models/, and the platform generates a links::store module from that link, so the part's code gets a typed Couchbase handle without naming a host.

A component then binds the parts it needs — the api's variant says models = ["todos-core"]. The separation is what lets the same logic serve an HTTP surface and a command-line client: the part does not know either exists.

The api

The component says what it serves and in which languages clients are generated:

kind = "online-service"

[interfaces.todos]
kind      = "bluetext-api"
languages = ["rust", "ts"]
base-path = "/api"

kind = "bluetext-api" is what makes this a surface the platform generates clients for, and languages says which. The TypeScript client is what the page imports and the Rust one what the command-line client imports, both from these declarations, so removing an endpoint breaks both consumers at compile time. base-path is the prefix the surface answers at, and the one consumers resolve to through the gateway.

The endpoints themselves are a file, one table per endpoint:

part         = "todos-core"
caller       = { admit = "admit_anyone" }
auth-refusal = "todos-auth-refusal-wire"
timeout      = "3s"

[list-todos]
method   = "GET"
path     = "/todos"
response = "todo-list-wire"
refusal  = "todo-refusal-wire"

[add-todo]
method   = "POST"
path     = "/todos"
request  = "new-todo-wire"
response = "todo-wire"
refusal  = "todo-refusal-wire"

The keys above the first endpoint apply to the whole surface: part binds the logic that serves it, caller states who is admitted, auth-refusal the shape returned when admission fails, and timeout the deadline for every call.

Each endpoint names a request, a response and a refusal, and those name wire shapes declared in shapes/todos.toml. A refusal is required: an endpoint declares how it fails as part of its contract, so a failure a caller can expect is a typed answer rather than a 500.

The variant says what the api is made from:

kind       = "source"
toolchain  = "cargo"
base-image = "gcr.io/distroless/cc"
models     = ["todos-core"]
serves.todos = { port = 8080 }

kind = "source" means the platform builds an image from our code, using base-image as the base and toolchain to build; models binds the parts; and serves.todos gives the interface its port. A component wrapping somebody else's software says kind = "prebuilt" and image instead, which is what Couchbase, Curity, Postgres and Kong each do.