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.

Running a Stack

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

This guide shows how to run a generated live or program-only spec() using a4-server. The code-first server does not load a StackManifest at startup or select a hosted Program Release.

Before running a live projection, configure your Yellowstone connection:

Terminal window
export YELLOWSTONE_ENDPOINT="https://your-geyser-endpoint.com"
export YELLOWSTONE_X_TOKEN="your-secret-token"

Or create a .env file in your project root:

.env
YELLOWSTONE_ENDPOINT=https://your-geyser-endpoint.com
YELLOWSTONE_X_TOKEN=your-secret-token

A program-only spec() serving typed account reads does not start the live runtime and does not need Yellowstone. Its HTTP reads use the configured Solana read RPC instead.

Add dependencies to your Cargo.toml:

Cargo.toml
[dependencies]
your-stack = { path = "../path/to/your/stack" }
a4-server = "0.20.1"
tokio = { version = "1.0", features = ["full"] }
anyhow = "1.0"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
dotenvy = "0.15"
# Required for TLS
rustls = { version = "0.23", default-features = false, features = ["ring"] }

Create your main.rs:

src/main.rs
use a4_server::Server;
use your_stack as my_stream;
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Install TLS provider (required for gRPC)
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
// Load .env file if present
dotenvy::dotenv().ok();
// Initialize logging
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "info".into()),
)
.init();
// Get the generated spec. It includes local account readers and automatic
// release fingerprints as well as this stack's live projection.
let spec = my_stream::spec();
// Start the server
Server::builder()
.spec(spec)
.websocket()
.bind("[::]:8877".parse::<SocketAddr>()?)
.health_monitoring()
.start()
.await?;
Ok(())
}

The generated local release fingerprint is derived from the ProgramSpec and decoder-engine contract. It is not a hosted Program Release and requires no decoder binding.

An IDL-only Rust module generates a spec() with typed account readers and no live projection bytecode:

use arete::prelude::*;
use a4_server::{RuntimePlan, Server};
use std::net::SocketAddr;
#[arete(idl = ["idl/my_program.json"])]
mod my_program {}
Server::builder()
.spec(my_program::spec())
.program_reads()
.http_bind("0.0.0.0:8081".parse::<SocketAddr>()?)
// http_bind configures the listener; restore the exact capability plan.
.runtime_plan(RuntimePlan::program_reads())
.start()
.await?;

Live, Program Read, chain, and transaction transports are independent. Enable only the capabilities this process should own:

use a4_server::{RuntimePlan, Server, TransactionConfig};
let plan = RuntimePlan {
health: true,
chain_reads: true,
program_reads: true,
stack_queries: false,
transactions: true,
websocket: true,
live_runtime: true,
};
Server::builder()
.spec(my_stream::spec())
.websocket() // live views
.bind("[::]:8877".parse::<SocketAddr>()?)
.program_reads() // typed program account HTTP reads
.chain_reads() // generic chain-read HTTP routes
.http_bind("0.0.0.0:8081".parse::<SocketAddr>()?)
.transactions_config(TransactionConfig {
enabled: true,
rpc_url: Some(transaction_rpc_url),
..TransactionConfig::default()
})
.runtime_plan(plan)
.start()
.await?;

.http() is available as a convenience for health, Program Read, chain-read, and stack-query routes. It still does not enable transaction submission.

Terminal window
cargo run --release

You should see output like:

INFO a4_server: Starting WebSocket server on [::]:8877
INFO a4_server: Connected to Yellowstone gRPC
INFO a4_server: Health monitoring enabled

Once running, connect using any Arete SDK:

TypeScript
import { Arete } from "@usearete/sdk";
import { MY_STREAM_STACK } from "./generated/my-stream-stack";
const stack = await Arete.connect(MY_STREAM_STACK, {
url: "ws://localhost:8877",
});

This local example assumes the server is explicitly configured for allow_all. Hosted browser deployments require a publishable key and should pass it through React as <AreteProvider stack={MY_STREAM_STACK} auth={{ publishableKey }}>.

For a composed client, configure each aliased LiveSpec’s WebSocket endpoint and each program’s Program Read binding separately. Supply chain and transaction transports explicitly; never derive them from the live URL.

Rust
use a4_sdk::Arete;
let stack = Arete::connect("ws://localhost:8877").await?;

Enable HTTP health checks for orchestrators like Kubernetes. Because the legacy http_health() and health_bind() helpers configure the shared HTTP listener, set an explicit plan when this process should expose health only:

use a4_server::RuntimePlan;
Server::builder()
.spec(spec)
.websocket()
.bind("[::]:8877".parse()?)
.health_monitoring()
.http_health()
.health_bind("0.0.0.0:8081".parse()?)
.runtime_plan(RuntimePlan {
health: true,
websocket: true,
live_runtime: true,
..RuntimePlan::default()
})
.start()
.await?;

Enable OpenTelemetry for Prometheus metrics:

Cargo.toml
a4-server = { version = "0.20.1", features = ["otel"] }

a4-server handles SIGINT and SIGTERM automatically, ensuring clean disconnection from the Yellowstone stream.

Building this binary does not publish a generated SDK package or configure a hostname. Operators publish reviewed npm/crates.io packages manually and map the server’s endpoint bindings through their chosen DNS/CDN provider. Arete’s portable artifacts do not require a particular DNS vendor or URL pattern.

To expose bounded transaction inspection and submission through the server’s HTTP endpoint, follow Transaction Relay. The relay is disabled by default and should use signed-session HTTP authentication in production.

The Yellowstone gRPC stream is bandwidth-intensive. Ensure your environment has:

  • Sufficient network throughput
  • CPU capacity for block deserialization
  • Stable, low-latency connection to your Yellowstone provider