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.mdto the URL or sendingAccept: 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.
Write Rust
Define entities using #[arete] macro
Build Stack
Emit ProgramSpec, LiveSpec, and StackManifest
Deploy via CLI
Deploy the exact manifest with the CLI
Connect from App
Use generated SDK to stream
Step 1: Write Your Stack Definition
Section titled “Step 1: Write Your Stack Definition”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
Step 2: Build the Stack
Section titled “Step 2: Build the Stack”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.
cargo buildAfter 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.jsonThe 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:
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.jsonOmit --selected-view to expose every view from each supplied LiveSpec.
Step 3: Deploy with the CLI
Section titled “Step 3: Deploy with the CLI”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.
# Initialize project config (creates arete.toml)a4 init
# Deploy your exact manifesta4 up .arete/OreStack.stack-manifest.jsonOn success, you’ll receive a WebSocket URL:
✔ Stack pushed (v1)✔ Build completed🚀 Deployment and endpoint bindings are ready→ CLI Reference — Full command documentation
Step 4: Generate SDK and Connect
Section titled “Step 4: Generate SDK and Connect”Generate a typed SDK from the same manifest you reviewed and deployed:
a4 sdk create --manifest .arete/OreStack.stack-manifest.json --tsa4 sdk create --manifest .arete/OreStack.stack-manifest.json --rustThis 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:
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 componentconst stack = useArete(ORE_STREAM_STACK);const { data: rounds } = stack.views.OreRound.list.use();npm install @usearete/sdkimport { Arete } from "@usearete/sdk";import { ORE_STREAM_STACK } from "./generated/ore-stack"; // Generated in Step 4
const a4 = await Arete.connect(ORE_STREAM_STACK, { auth: { publishableKey: process.env.ARETE_PUBLISHABLE_KEY! },});
for await (const round of a4.views.OreRound.list.use()) { console.log("Round updated:", round);}use a4_sdk::prelude::*;use ore_stack::{OreStack, OreRound}; // Generated in Step 4
#[tokio::main]async fn main() -> anyhow::Result<()> { // Connect to your deployed stack let a4 = Arete::<OreStack>::connect().await?;
// Stream updates via typed views let mut stream = a4.views.ore_round.latest().listen();
while let Some(round) = stream.next().await { println!("Round #{:?}: motherlode={:?}", round.id.round_id, round.state.motherlode); }
Ok(())}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
Next Steps
Section titled “Next Steps”| Goal | Page |
|---|---|
| Understand the DSL in depth | Stack Definitions |
| Set up your development environment | Installation |
| Build a complete example | Your First Stack |
| Learn all available macros | Macro Reference |
| Master aggregation strategies | Population Strategies |