The Spec
A spec is a single YAML document that declares everything meaningful about your application: its data model, business rules, API surface, access policy, and behavioral tests.
Why this exists
Application behavior is typically distributed across database migrations, route handlers, validation schemas, and test fixtures. No single artifact describes what an application is. Onboarding a new engineer requires reading across dozens of files to reconstruct the full picture. Portability is impossible when business rules are embedded in framework middleware.
The spec is the response to this problem. It is the authoritative description of your application. The compiler validates it and produces deterministic artifacts. The runtime executes those artifacts. The raw YAML is never read at runtime.
The eleven sections
Every spec has a fixed set of sections. Each section has a defined owner and a clear mandate.
| Section | Owner | What it declares |
|---|---|---|
app | Platform | Identity, timezone, history policy |
domain | Business | Data model, business logic, read models |
session | User | Ephemeral workspace state |
views | Surface | Logical render graph |
queries | Public API | Typed read entrypoints |
mutations | Public API | Typed write entrypoints |
routes | Navigation | Named navigable entrypoints into views |
personas | Quality | Named customer archetypes |
scenarios | Quality | Behavioral test contracts |
wiring | Infrastructure | Surface bind maps and deployment requirements |
access | Security | Role definitions and default access policy |
Everything above wiring is pure declaration. It is runtime-agnostic and never references a specific vendor, SDK, or deployment environment.
The app section
The app section declares your application's identity and platform-level policy.
app:
id: my-first-app
name: "My First App"
tz: UTC
history:
enabled: true
retention: 365d
id must be stable across deployments. It is used in artifact hashing and replay key namespacing. tz controls timestamp normalization across the kernel. Setting history.enabled: true activates the timeline projection strategy.
The field type system
The spec uses a shorthand type syntax wherever fields are declared.
| Syntax | Meaning |
|---|---|
text | String, nullable |
text! | String, required |
int | Integer, nullable |
int! | Integer, required |
bool | Boolean, nullable |
bool! | Boolean, required |
id | Opaque identifier, nullable |
id! | Opaque identifier, required |
timestamp | ISO 8601 datetime, nullable |
timestamp! | ISO 8601 datetime, required |
object | Nested object with inline sub-fields |
The ! suffix means required (non-null). Object types use inline sub-field declarations:
moderation:
object:
allowed: bool!
reason: text
What the spec cannot do
These are intentional constraints, not oversights.
- The spec cannot execute code. There are no function calls or imports. The
$exprDSL is a restricted expression language and cannot call arbitrary functions. - The spec cannot reference external systems by name. Supabase, Redis, Stripe: none of these appear in the spec. They appear in the lockfile, which the capability binding resolver produces.
- Projections cannot be called directly. They are always wrapped by a named query. This enforces the implementation/contract separation.
- Internal actions cannot be called from entrypoints. An action with no corresponding mutation is only callable by flows and other actions.
What's next
- Domain Model: collections, actions, signals, flows, and projections
- Queries and Mutations: typed entrypoints into your domain
- Surfaces and Wiring: how the spec connects to HTTP, web, CLI, and webhooks
- Scenarios: behavioral tests that are part of the spec itself