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.
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.
1. Set Environment Variables
Section titled “1. Set Environment Variables”Before running a live projection, configure your Yellowstone connection:
export YELLOWSTONE_ENDPOINT="https://your-geyser-endpoint.com"export YELLOWSTONE_X_TOKEN="your-secret-token"Or create a .env file in your project root:
YELLOWSTONE_ENDPOINT=https://your-geyser-endpoint.comYELLOWSTONE_X_TOKEN=your-secret-tokenA 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.
2. Create the Server Binary
Section titled “2. Create the Server Binary”Add dependencies to your 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 TLSrustls = { version = "0.23", default-features = false, features = ["ring"] }Create your 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.
Program-only server
Section titled “Program-only server”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?;Choose runtime surfaces explicitly
Section titled “Choose runtime surfaces explicitly”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.
3. Run the Server
Section titled “3. Run the Server”cargo run --releaseYou should see output like:
INFO a4_server: Starting WebSocket server on [::]:8877INFO a4_server: Connected to Yellowstone gRPCINFO a4_server: Health monitoring enabled4. Connect Clients
Section titled “4. Connect Clients”Once running, connect using any Arete SDK:
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.
use a4_sdk::Arete;
let stack = Arete::connect("ws://localhost:8877").await?;Production Tips
Section titled “Production Tips”Health Endpoints
Section titled “Health Endpoints”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?;Metrics
Section titled “Metrics”Enable OpenTelemetry for Prometheus metrics:
a4-server = { version = "0.20.1", features = ["otel"] }Graceful Shutdown
Section titled “Graceful Shutdown”a4-server handles SIGINT and SIGTERM automatically, ensuring clean disconnection from the Yellowstone stream.
Deployment, Packages, and DNS
Section titled “Deployment, Packages, and DNS”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.
Transaction Relay
Section titled “Transaction Relay”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.
Resource Considerations
Section titled “Resource Considerations”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