Capabilities
A capability is a named, typed operation contract. Capabilities are how actions interact with the outside world — or with reusable logic defined inside the spec itself.
There are two kinds of capabilities: domain capabilities and provider capabilities. They share the same call syntax in action steps but have fundamentally different implementations.
Domain capabilities
A domain capability is authored in the spec and executed in-process by the domain runtime. It is a pure function: given an input, it returns an output with no side effects.
domain:
capabilities:
message.is_allowed:
in:
message: text!
out:
allowed: bool!
reason: text
do:
- expr:
"!contains":
- to_lower: { var: [input.message, ""] }
- spam
as: allowed
return:
allowed: { $expr: { var: allowed } }
reason:
$expr:
if:
- var: allowed
- null
- "Blocked by moderation policy"
The do block is a sequence of expr steps that compute intermediate values. The return block produces the final output using $expr.
Domain capabilities have no access to provider capabilities. They cannot perform network calls, write to collections, or emit signals. This constraint is enforced at compilation.
Provider capabilities
A provider capability is an external operation contract fulfilled by a provider resolved at deploy time. Provider capabilities are declared in the domain.capabilities section only if they are used — their implementation is never authored in the spec.
Common provider capabilities:
| Capability | Description |
|---|---|
ids.generate | Generate one or more unique IDs |
collections.write | Write a record to a collection |
collections.read | Read a record from a collection by ID |
notifications.send | Deliver a notification to a user |
session.read | Read from the current user's session |
session.write | Write to the current user's session |
Provider capabilities are bound in the wiring.requirements section, not in domain.capabilities.
Calling a capability from an action
Both capability kinds are called the same way inside an action's do block.
domain:
actions:
guestbook.submit:
do:
- call: ids.generate
with: { prefix: "msg_", count: 1 }
as: generated_ids
- call: message.is_allowed
with:
message: { $expr: { var: input.message } }
as: moderation
- call: collections.write
with:
patch:
op: upsert
collection: hello_messages
id: { $expr: { var: generated_ids.ids.0 } }
value:
id: { $expr: { var: generated_ids.ids.0 } }
message: { $expr: { var: input.message } }
user_id: { $expr: { var: principal.subject } }
created_at: { $expr: { var: ctx.now } }
The as field binds the capability's output to a named variable available in subsequent steps. Variables are accessed with var: <name>.<field>.
What's next
- Actions: how to compose capability calls into atomic commands
- Capability binding: how providers implement capabilities