Arete documentation
Your First Stack
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.
For AI agents: the documentation index is at llms.txt (full corpus: llms-full.txt). A markdown source for this page is /building-stacks/your-first-stack.md.
In this tutorial, you’ll build an end-to-end streaming data pipeline in 15 minutes. We’ll create an “Ore” stack that tracks ORE mining rounds and displays live round data in a React app.
What You’ll Build
Section titled “What You’ll Build”A full-stack streaming application consisting of:
- A Rust Definition: Defines how to transform on-chain ORE mining data into a queryable “OreRound” entity.
- A Cloud Deployment: A managed Arete instance that processes the stream.
- A React Frontend: A live dashboard showing the latest ORE mining rounds that updates instantly as on-chain events occur.
Part 1: Create the Stack (Rust)
Section titled “Part 1: Create the Stack (Rust)”The stack definition is the heart of your application. It tells Arete which on-chain data to watch and how to project it into a useful state.
1. Create a new Rust project
Section titled “1. Create a new Rust project”cargo new ore-stack --libcd ore-stack2. Add dependencies
Section titled “2. Add dependencies”Add arete to your Cargo.toml:
[dependencies]arete = "0.51.2"serde = { version = "1.0", features = ["derive"] }3. Get the ORE IDL
Section titled “3. Get the ORE IDL”Download the ORE program IDL file and place it in an idl/ directory:
mkdir -p idlcurl -o idl/ore.json https://docs.arete.run/ore.json4. Write your stack definition
Section titled “4. Write your stack definition”Open src/lib.rs and define your projection. We’ll create a simplified version of the ORE mining round tracker:
use arete::prelude::*;
#[arete(idl = "idl/ore.json")]pub mod ore_stream { use arete::macros::Stream; use serde::{Deserialize, Serialize};
// The main entity representing an ORE mining round #[entity(name = "OreRound")] #[view(name = "latest", sort_by = "id.round_id", order = "desc")] pub struct OreRound { pub id: RoundId, pub state: RoundState, pub metrics: RoundMetrics, }
// Round identification with primary key #[derive(Debug, Clone, Serialize, Deserialize, Stream)] pub struct RoundId { #[map(ore_sdk::accounts::Round::id, primary_key, strategy = SetOnce)] pub round_id: u64,
#[map(ore_sdk::accounts::Round::__account_address, lookup_index, strategy = SetOnce)] pub round_address: String, }
// Round state fields - updated on every change #[derive(Debug, Clone, Serialize, Deserialize, Stream)] pub struct RoundState { #[map(ore_sdk::accounts::Round::expires_at, strategy = LastWrite)] pub expires_at: Option<u64>,
#[map(ore_sdk::accounts::Round::total_deployed, strategy = LastWrite)] pub total_deployed: Option<u64>,
#[map(ore_sdk::accounts::Round::total_winnings, strategy = LastWrite)] pub total_winnings: Option<u64>, }
// Aggregated metrics computed from transactions #[derive(Debug, Clone, Serialize, Deserialize, Stream)] pub struct RoundMetrics { // Count of deploy instructions for this round #[aggregate(from = ore_sdk::instructions::Deploy, strategy = Count, lookup_by = accounts::round)] pub deploy_count: Option<u64>,
// Sum of all deployed SOL amounts #[aggregate(from = ore_sdk::instructions::Deploy, field = amount, strategy = Sum, lookup_by = accounts::round)] pub total_deployed_sol: Option<u64>, }}5. Build to generate explicit artifacts
Section titled “5. Build to generate explicit artifacts”When you compile your code, Arete macros write the portable artifacts directly:
cargo buildThe generated ore_stream::spec() follows the OSS code-first path. It embeds
the live runtime plus generated account readers and automatically derives local
release fingerprints from the ProgramSpec and decoder-engine contract. It does
not require a hosted Program Release or decoder selection.
Part 2: Deploy to Arete Cloud
Section titled “Part 2: Deploy to Arete Cloud”Now let’s push your stack to the cloud. Arete manages the infrastructure, so you don’t need to worry about scaling or WebSocket servers.
1. Initialize your project
Section titled “1. Initialize your project”This creates a arete.toml file that links your local stack to the cloud.
a4 init2. Push and Deploy
Section titled “2. Push and Deploy”The up command deploys one exact StackManifest. Hosted preparation resolves
its ProgramSpec to an immutable Program Release and creates deployment and
endpoint bindings without adding those operational values to the manifest.
a4 up .arete/OreStream.stack-manifest.jsonExpected Output:
✔ Stack pushed (v1)✔ Build created (id: bld_123...)✔ Build completed🚀 Deployment and endpoint bindings are readyPart 3: Generate SDK and Connect
Section titled “Part 3: Generate SDK and Connect”Finally, let’s build the frontend. First, generate a typed SDK from your deployed stack, then connect using React or TypeScript.
1. Generate the SDK
Section titled “1. Generate the SDK”From your stack project directory:
a4 sdk create --manifest .arete/OreStream.stack-manifest.json \ --ts \ --output ./sdk/ore-stack.tsThis creates a typed SDK with full TypeScript support. Use --rust instead of
--ts for Rust output. Generation writes local source; publishing it as an npm
package or Rust crate is a separate operator action.
Scaffold a React Vite app for the stack
Section titled “Scaffold a React Vite app for the stack”npm create vite@latest my-ore-app -- --template react-tscd my-ore-appnpm install @usearete/react @usearete/sdk zod@usearete/react carries zustand as a normal dependency; do not install it separately.
Copy the generated SDK into your React app:
cp -r ../ore-stack/sdk ./src/ore-sdkConfigure the React AreteProvider
Section titled “Configure the React AreteProvider”Hosted browser clients require a publishable key even for read-only viewing. A wallet is not required. Create .env.local:
VITE_ARETE_PUBLISHABLE_KEY=hspk_...Wrap your app in AreteProvider with the generated stack and authentication. The stack already contains its deployment endpoints.
import { AreteProvider } from "@usearete/react";import { ORE_STREAM_STACK } from "./ore-sdk";
const publishableKey = import.meta.env.VITE_ARETE_PUBLISHABLE_KEY;if (!publishableKey) throw new Error("VITE_ARETE_PUBLISHABLE_KEY is required");
const root = ReactDOM.createRoot(document.getElementById("root")!);root.render( <AreteProvider stack={ORE_STREAM_STACK} auth={{ publishableKey }}> <App /> </AreteProvider>,);React: Use the Generated Stack
Section titled “React: Use the Generated Stack”import { useArete } from "@usearete/react";import { ORE_STREAM_STACK } from "./ore-sdk";
export default function App() { const arete = useArete(ORE_STREAM_STACK); const rounds = arete.views.OreRound.latest.use({ take: 5, });
if (arete.status === "error") { return <button onClick={() => void arete.retry()}>Reconnect</button>; } if (rounds.status === "connecting" || rounds.status === "subscribing") { return <div>Connecting to stream...</div>; } if (rounds.status === "error") return <div>{rounds.error?.message}</div>; if (rounds.isEmpty) return <div>No rounds yet.</div>;
return ( <div> <h1>Latest ORE Mining Rounds</h1> {arete.socketIssue && <p>{arete.socketIssue.message}</p>} {rounds.isRefreshing && <p>Refreshing stream data...</p>} {rounds.data?.map((round) => ( <div key={round.id.roundId?.toString() ?? round.id.roundAddress ?? "unknown"} style={{ padding: "1rem", border: "1px solid #ccc", margin: "0.5rem 0", }} > <h3>Round #{round.id.roundId?.toString() ?? "unknown"}</h3> <p>Address: {round.id.roundAddress ?? "unknown"}</p> <p> Total Deployed: {round.state.totalDeployed?.toLocaleString()}{" "} SOL </p> <p> Total Winnings: {round.state.totalWinnings?.toLocaleString()}{" "} SOL </p> <p>Deploy Count: {round.metrics.deployCount?.toString() ?? "0"}</p> </div> ))} </div> );}Scaffold a TypeScript Node project for the stack
Section titled “Scaffold a TypeScript Node project for the stack”mkdir my-ore-app && cd my-ore-appnpm init -ynpm pkg set type=modulenpm install @usearete/sdk zodnpm install -D typescript tsx @types/nodeCopy the generated SDK:
cp -r ../ore-stack/sdk ./src/ore-sdkStream ORE rounds from TypeScript
Section titled “Stream ORE rounds from TypeScript”import { Arete } from "@usearete/sdk";import { ORE_STREAM_STACK } from "./ore-sdk";
async function main() { // Connect to your deployed stack using the generated SDK. Server-side code // passes no auth option: the SDK uses ARETE_API_KEY if set, else the key of // your a4 login. const a4 = await Arete.connect(ORE_STREAM_STACK);
console.log("Connected! Streaming ORE rounds...\n");
// Stream updates with full type safety for await (const update of a4.views.OreRound.latest.watch()) { if (update.type === "upsert") { const round = update.data; console.log(`Round #${round.id.roundId}:`); console.log(` Address: ${round.id.roundAddress}`); console.log( ` Total Deployed: ${round.state.totalDeployed?.toLocaleString()} SOL`, ); console.log( ` Total Winnings: ${round.state.totalWinnings?.toLocaleString()} SOL`, ); console.log(` Deploy Count: ${round.metrics.deployCount ?? 0}`); console.log(""); } }}
main().catch(console.error);TypeScript: Run your app
Section titled “TypeScript: Run your app”npx tsx src/index.tsOn a machine logged in with a4 (after a4 init or a4 auth signup) this
needs no key setup. Elsewhere, such as on a server, set ARETE_API_KEY to an
agent or secret key: ARETE_API_KEY=a4_sk_... npx tsx src/index.ts.
Troubleshooting
Section titled “Troubleshooting””Stack file not found”
Section titled “”Stack file not found””Ensure you ran cargo build in your Rust project and that
.arete/OreStream.stack-manifest.json exists. The #[arete] macro writes the
ProgramSpec, LiveSpec, and StackManifest during compilation.
WebSocket connection fails
Section titled “WebSocket connection fails”- Check that the live binding matches the output of
a4 up .arete/OreStream.stack-manifest.json. - Ensure your stack is successfully deployed by running
a4 stack list.
Data is not appearing
Section titled “Data is not appearing”- Verify that the ORE program is emitting events and updating the Round accounts.
- Check the browser console for any subscription errors.
- Ensure your view path matches the entity name exactly (e.g.,
OreRound/latest).
Next Steps
Section titled “Next Steps”- Learn more about Stack Macros to build complex pipelines.
- Explore Population Strategies for different data access patterns.
- Check the CLI Reference for advanced deployment options.