Quickstart: Concert App
Requires a Causet runtime. This quickstart uses Causet Cloud (Early access). Self-hosted Docker is Coming soon. To learn the model without a runtime, try the 5-Minute Quickstart first. See What runs today?.
In this guide you build the concert app used throughout the getting-started tutorials: users follow artists, artists announce shows, and a query returns upcoming shows for artists a user follows.
You will author every layer of the Causet DSL in order:
- State (Entities) — what exists
- Events — immutable facts
- Intents (Actions) — commands and rules
- Projections — read models from events
- Queries — named reads over projections
- Relationships — graph edges between entities
Then compile, deploy, submit intents, and run a query.
Prerequisites: Install Causet (CLI + Causet Cloud early-access account).
App setup
causet initWhen prompted:
| Prompt | Choose |
|---|---|
| Project name | concert-app |
| Template | Concert app |
cd concert-appThe Concert app template scaffolds app.causet and starter DSL files for every layer below. This page walks through what each file does — open them in your new project as you read.
# app.causet
dsl_version: 1
app: concert_app
includes:
states: [./states/**/*.state.causet]
events: [./events/**/*.events.causet]
actions: [./actions/**/*.actions.causet]
projections: [./projections/**/*.projections.causet]
queries: [./queries/**/*.queries.causet]
relationships: [./relationships/**/*.relationships.causet]Files use the .causet extension. The actions: key in the manifest defines intents — the docs call them intents; the DSL section is named actions:.
1. State (Entities)
Entities are typed records the runtime stores per ID. Define state before events or intents — everything else references these types.
# states/user.state.causet
state:
user:
entity_key: user_id
fields:
- name: username
type: string
default: ""
- name: following_count
type: int
default: 0# states/artist.state.causet
state:
artist:
entity_key: artist_id
fields:
- name: name
type: string
default: ""
- name: follower_count
type: int
default: 0Each entity instance is addressed by (stream, entity_id) — e.g. user user-1 on the user stream.
See State & Memory for snapshots, replay, and entity keys.
2. Events
Events are past-tense facts appended to the ledger. Register every event type before referencing it in intents or projections.
# events/follow.events.causet
events:
ARTIST_FOLLOWED:
state: user
entity_expr: event.user_id
payload:
user_id: string
artist_id: string
ARTIST_UNFOLLOWED:
state: user
entity_expr: event.user_id
payload:
user_id: string
artist_id: string# events/show.events.causet
events:
SHOW_ANNOUNCED:
state: artist
entity_expr: event.artist_id
payload:
artist_id: string
show_id: string
venue: string
date: string
title: stringNever use reserved payload names: type, ts, entity_id — the runtime sets those.
See Events for naming, metadata, and versioning.
3. Intents (Actions)
Intents are commands your app submits. Rules run in three phases: preflight → core → side_effects.
# actions/follow.actions.causet
actions:
FOLLOW_ARTIST:
state: user
entity_id_expr: intent.user_id
input:
user_id: { type: string, required: true }
artist_id: { type: string, required: true }
preflight:
rules:
- name: reject_self_follow
when: { expr: "intent.user_id == intent.artist_id" }
then:
- op: reject
code: CANNOT_FOLLOW_SELF
core:
rules:
- name: increment_following
when: {}
then:
- op: add
path: /following_count
value: 1
side_effects:
rules:
- name: emit_followed
then:
- op: emit
event_type: ARTIST_FOLLOWED
payload:
user_id: intent.user_id
artist_id: intent.artist_id# actions/show.actions.causet
actions:
ANNOUNCE_SHOW:
state: artist
entity_id_expr: intent.artist_id
input:
artist_id: { type: string, required: true }
show_id: { type: string, required: true }
venue: { type: string, required: true }
date: { type: string, required: true }
title: { type: string, required: true }
core:
rules:
- name: emit_announced
when: {}
then:
- op: emit
event_type: SHOW_ANNOUNCED
payload:
artist_id: intent.artist_id
show_id: intent.show_id
venue: intent.venue
date: intent.date
title: intent.titleSee Intents for rule phases and the full operations reference.
4. Projections
Projections are PostgreSQL read models maintained from events. Always declare fields: with explicit types.
# projections/follow.projections.causet
projections:
user_following:
source_events: [ARTIST_FOLLOWED, ARTIST_UNFOLLOWED]
target:
table: user_following
primary_key: [user_id, artist_id]
fields:
user_id: TEXT
artist_id: TEXT
followed_at: BIGINT
derive:
user_id: event.user_id
artist_id: event.artist_id
followed_at: event.ts
mutations:
ARTIST_FOLLOWED: { op: upsert }
ARTIST_UNFOLLOWED: { op: delete }
artist_show_directory:
source_events: [SHOW_ANNOUNCED]
target:
table: artist_show_directory
primary_key: [show_id]
fields:
show_id: TEXT
artist_id: TEXT
title: TEXT
venue: TEXT
date: TEXT
created_at: BIGINT
derive:
show_id: event.show_id
artist_id: event.artist_id
title: event.title
venue: event.venue
date: event.date
created_at: event.ts
mutations:
SHOW_ANNOUNCED: { op: upsert }See Projections for derive, aggregates, and indexes.
5. Queries
Queries are named, parameterized reads over projection tables — no hand-written SQL.
# queries/follow.queries.causet
queries:
shows_for_followed_artists:
from: artist_show_directory
input:
user_id: { type: string, required: true }
joins:
user_following:
on:
artist_show_directory.artist_id: user_following.artist_id
fields:
- user_following.user_id
fields:
- artist_show_directory.*
where:
user_following.user_id: { eq: input.user_id }
order_by:
date: asc
limit: 50See Queries for filters, joins, and aggregates.
6. Relationships
Relationships declare graph edges between entity types. Here, a user follows an artist — backed by the same follow/unfollow events.
# relationships/follow.relationships.causet
relationships:
artist_followers:
from: user
to: artist
cardinality: many_to_many
unique: true
emit_events:
created: ARTIST_FOLLOWED
removed: ARTIST_UNFOLLOWEDRelationships enable graph traversals and enforce edge uniqueness. Event emission in the intent still drives projections and the ledger.
See Relationships for cardinality and emit event wiring.
Compile
causet build validate --runtime .
causet build compile --runtime . --out dist/Expected artifacts in dist/:
ruleset.vrd
causet.projections.json
manifest.jsonFix compiler errors before continuing — common issues: missing projection fields:, undeclared event types in source_events, reserved payload field names.
Deploy
causet context use platform my-platform
causet context use app concert-app
causet release create --input dist/ --tag 1.0.0
causet release publish --tag 1.0.0
causet deploy apply --tag 1.0.0 --fork main
causet deploy activate --tag 1.0.0 --fork main --mode FULLOr one command — causet deploy runs compile, release, publish, and activate as a single golden path:
causet deploy --fork main --runtime . --fullDeploy applies projection DDL to schema my_platform_concert_app_main — creating user_following and artist_show_directory.
See Deployments for forks, releases, and staging workflows.
Run it
Follow an artist:
causet intent FOLLOW_ARTIST \
--fork main \
--stream user_stream \
--entity user-1 \
--payload '{"user_id":"user-1","artist_id":"artist-pearl-jam"}'Announce a show:
causet intent ANNOUNCE_SHOW \
--fork main \
--stream artist_stream \
--entity artist-pearl-jam \
--payload '{"artist_id":"artist-pearl-jam","show_id":"show-pj-brooklyn-2026","venue":"Barclays Center","date":"2026-09-15","title":"Pearl Jam - Dark Matter Tour"}'Wait a moment for projection materialization, then query:
causet query shows_for_followed_artists \
--fork main \
--param user_id=user-1You should see the Pearl Jam show in the response.
What just happened
- Intent
FOLLOW_ARTISTran preflight, mutated entity state, and emittedARTIST_FOLLOWED. - The projection worker UPSERTed a row into
user_following. - Intent
ANNOUNCE_SHOWemittedSHOW_ANNOUNCED→artist_show_directory. - Query joined both tables and returned shows for followed artists.
Next steps
Layer-by-layer tutorials (same concert app from causet init):
| Tutorial | Page |
|---|---|
| Set up | Your First State — causet init → Concert app |
| State | Your First State |
| Events | Your First Event |
| Intents | Your First Intent |
| Projections | Your First Projection |
| Queries | Your First Query |
| Relationships | Your First Relationship |
| Workflows | Your First Workflow |
| Full app | Complete Concert App |
| Topic | Page |
|---|---|
| Intent rules in depth | Intents |
| Deploy to staging | Deployments |
| Extended concert domain | Concert App |
| No CLI/Cloud access yet? | 5-Minute Quickstart — browser only |