Skip to content
For the complete documentation index optimized for AI agents, see llms.txt or llms-full.txt. A markdown version of this page is available by appending .md to the URL or sending Accept: text/markdown.

Workflow

For AI agents: the documentation index is at llms.txt (full corpus: llms-full.txt). A markdown source for this page is /building-stacks/workflow.md.

Building a stack follows a straightforward four-step workflow: define your data model in Rust, compile portable artifacts, deploy an exact StackManifest, and connect from your application.

1

Write Rust

Define entities using #[arete] macro

2

Build Stack

Emit ProgramSpec, LiveSpec, and StackManifest

3

Deploy via CLI

Deploy the exact manifest with the CLI

4

Connect from App

Use generated SDK to stream


A stack definition is a Rust module that maps structure from the IDL into a rich, queryable state ready to consume in your application layer. Using Arete’s expressive DSL, you define entities, field mappings, aggregations, computed fields, relationships, and more — all in declarative Rust syntax.

use arete::{arete, Stream};
#[arete(idl = "my_program.json")]
pub mod ore_stack {
#[entity]
#[derive(Stream)]
pub struct OreRound {
#[map(RoundState::round_id, primary_key)]
pub round_id: u64,
#[map(RoundState::motherlode)]
pub motherlode: u64,
#[map(RoundState::difficulty)]
pub difficulty: u64,
}
}

The Rust code is purely declarative—you’re describing what data you want, not how to fetch it. Arete handles all the account parsing, event processing, and state management.

Stack Definitions — Learn the full DSL syntax


When you compile your Rust project, Arete macros transform the definition directly into three explicit, content-addressed artifact kinds:

  • ProgramSpec — endpoint-free program identity, public IDL, accounts, instructions, and PDAs.
  • LiveSpec — entity, mapping, handler, resolver, and view behavior over exact ProgramSpecs.
  • StackManifest — the client composition: aliased LiveSpecs, ProgramSpecs, and selected views. It contains no deployment URL.
Terminal window
cargo build

After building, you’ll find the generated specification in .arete/:

my-stack/
├── src/lib.rs
├── Cargo.toml
└── .arete/
├── ore.program-spec.json
├── OreStack.live-spec.json
└── OreStack.stack-manifest.json

The DSL can also generate only the program layer. An IDL-only module with no entities emits ProgramSpecs, a zero-live StackManifest, and a program-only spec() containing generated account readers:

#[arete(idl = ["my_program.json"])]
pub mod my_program {}
let spec = my_program::spec();

The program-only spec() is suitable for Server::builder().spec(spec) with Program Read HTTP routes and does not start projection work unless you enable a live runtime.

To combine independently built live packages, assign each LiveSpec a stable alias and optionally select an exact ordered view allowlist:

Terminal window
a4 stack compose --name my-app \
--live markets=./markets.live-spec.json \
--live positions=./positions.live-spec.json \
--artifact-dir . \
--selected-view markets=Market/list \
--selected-view positions=Position/state \
--output ./MyApp.stack-manifest.json

Omit --selected-view to expose every view from each supplied LiveSpec.


The Arete CLI (a4) handles hosted deployment. A single command submits one exact StackManifest. The control plane resolves its ProgramSpecs to immutable Program Releases, prepares each aliased LiveSpec, and creates deployment and endpoint bindings.

Terminal window
# Initialize project config (creates arete.toml)
a4 init
# Deploy your exact manifest
a4 up .arete/OreStack.stack-manifest.json

On success, you’ll receive a WebSocket URL:

✔ Stack pushed (v1)
✔ Build completed
🚀 Deployment and endpoint bindings are ready

CLI Reference — Full command documentation


Generate a typed SDK from the same manifest you reviewed and deployed:

Terminal window
a4 sdk create --manifest .arete/OreStack.stack-manifest.json --ts

This creates local source containing the selected stack definition, entities, views, and independent program SDKs. a4 sdk create does not publish an npm package or Rust crate; package publication is a separate operator action.

For composed manifests, each --live alias=path keeps its own live transport. Program Read bindings are selected per program, while chain and transaction transports are supplied independently. A client must not derive any of these endpoints from another. Import the generated source in your application:

Terminal window
npm install @usearete/react @usearete/sdk zod

@usearete/react installs zustand itself. Hosted browser connections require the publishable key shown below even for read-only use; a wallet is not required to view data.

import { AreteProvider, useArete } from "@usearete/react";
import { ORE_STREAM_STACK } from "./generated/ore-stack"; // Generated in Step 4
const publishableKey = import.meta.env.VITE_ARETE_PUBLISHABLE_KEY;
if (!publishableKey) throw new Error("VITE_ARETE_PUBLISHABLE_KEY is required");
// Wrap your app
<AreteProvider stack={ORE_STREAM_STACK} auth={{ publishableKey }}>
<App />
</AreteProvider>;
// In your component
const stack = useArete(ORE_STREAM_STACK);
const { data: rounds } = stack.views.OreRound.list.use();

Share the generated SDK with your team or publish it manually — anyone with the SDK can connect to the selected bindings when authorized. Hosted deployment does not prescribe a DNS provider; operators can hand returned endpoint bindings to their chosen DNS/CDN provider.

Your First Stack — Complete tutorial with working code


GoalPage
Understand the DSL in depthStack Definitions
Set up your development environmentInstallation
Build a complete exampleYour First Stack
Learn all available macrosMacro Reference
Master aggregation strategiesPopulation Strategies