Skip to main content

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.

SectionOwnerWhat it declares
appPlatformIdentity, timezone, history policy
domainBusinessData model, business logic, read models
sessionUserEphemeral workspace state
viewsSurfaceLogical render graph
queriesPublic APITyped read entrypoints
mutationsPublic APITyped write entrypoints
routesNavigationNamed navigable entrypoints into views
personasQualityNamed customer archetypes
scenariosQualityBehavioral test contracts
wiringInfrastructureSurface bind maps and deployment requirements
accessSecurityRole 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.

SyntaxMeaning
textString, nullable
text!String, required
intInteger, nullable
int!Integer, required
boolBoolean, nullable
bool!Boolean, required
idOpaque identifier, nullable
id!Opaque identifier, required
timestampISO 8601 datetime, nullable
timestamp!ISO 8601 datetime, required
objectNested 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 $expr DSL 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