RustAG
Advanced

Architecture

RustAG turns LiteSVM into a persistent, mainnet-mirroring staging environment. A request flows down through four layers — protocol, engine, mirror, and mainnet — with all policy living in the core.

Architecture overview

Instead of forking at a block hash (which Solana has no equivalent of), RustAG fetches mainnet accounts lazily on first access and tracks every local write so it knows what it may and may not refresh. A Solana client talks to a stagenet as if it were a cluster.

Request & data flow

four-layer request flow
  Solana client / wallet / Anchor / dashboard          │  JSON-RPC      WebSocket      REST          ▼                               ▼┌───────────────────────────────────────────────────────┐│ rustag-rpc  (axum)                                     ││   • JSON-RPC server   POST  /            (jsonrpc.rs)  ││   • WebSocket server  GET   /  accountSubscribe (ws.rs)││   • REST API          /api/*             (rest.rs)     ││     mounts one rustag_core::Stagenet behind RwLock     │└───────────────────────────────────────────────────────┘          │ send_transaction / get_account_info / airdrop┌───────────────────────────────────────────────────────┐│ rustag-core  (the engine)                              ││   • LiteSVM instance (execution)                       ││   • account-state machine: Unknown→Clean→Dirty/Pinned  ││   • lazy-mirror logic (pre_load_accounts_for_tx)       ││   • SQLite persistence via sqlx → survives restarts    ││   • background workers: oracle sync, metrics, realtime │└───────────────────────────────────────────────────────┘          │ getMultipleAccounts (cache miss / oracle refresh)┌───────────────────────────────────────────────────────┐│ rustag-mirror  (read-side, dependency-light)           ││   • raw JSON-RPC over reqwest (no solana-rpc-client)   ││   • MainnetMirror::fetch_multiple (≤100 keys/call)     ││   • known-program / oracle registry + RpcRateLimiter   ││   • RealtimeMirror push: accountSubscribe WS → mpsc    │└───────────────────────────────────────────────────────┘          │ HTTPS / WSS             Mainnet RPC (Helius / Triton / …)

rustag-mirror is a pure read-side that knows nothing about dirty/clean tracking — it just answers “give me the current mainnet state of these pubkeys.” All the policy (state machine, persistence, sync invariants) lives in rustag-core, and all the protocol surface lives in rustag-rpc.

What happens on a transaction

Stagenet::send_transaction runs four stages:

  1. Pre-load — extract the transaction's static account keys and batch-fetch any that are not already loaded and not Dirty from mainnet via the mirror, loading them into LiteSVM as Clean (fetch failures are logged and tolerated).
  2. Execute through LiteSVM with signature and blockhash checks on.
  3. Track writes — derive writable accounts from the message header layout, mark them Dirty, and persist their post-state.
  4. Index the transaction (signature, success, fee, compute units, programs, logs) for the dashboard and rustag logs.

For VersionedMessage::V0 transactions, prepare_accounts resolves address_table_lookups through the mirror before execution, so v0 DeFi transactions read real mainnet state instead of failing with LookupTableAccountNotFound.

Crate map

RustAG is a Cargo workspace under crates/. The dependency direction is rustag-cli → rustag-rpc → rustag-core → rustag-mirror; rustag-core re-exports the mirror surface so downstream crates have a single dependency. Every Phase 2/3 crate is pure Rust with no external service dependency.

CrateResponsibilityPhase
rustag-coreThe runtime: LiteSVM + AccountSync state machine + SQLite persistence + lazy-mirror engine. Central type Stagenet.1
rustag-mirrorMainnet fetcher: raw JSON-RPC over reqwest, known-program/oracle registry, RpcRateLimiter, and the realtime push source.1 · realtime 2
rustag-rpcSolana-compatible JSON-RPC + WebSocket + REST API, all on axum (serve, ServerAddrs, AppState).1
rustag-cliThe rustag binary (clap subcommands for every Phase 1/2/3 command).1 + 2/3
rustag-schedulerActivity Scheduler: pairs a Schedule (@every / cron) with an Action (airdrop / transfer / raw-tx).2
rustag-simSimulation: fork/replay/stress/compare, MEV/Jito bundles, invariant fuzzing, exploit scanning, differential execution.2 / 3
rustag-cloudMulti-tenant control plane: each stagenet an isolated child process behind a reverse proxy with API-key auth.2
rustag-attestVerifiable attestation: SHA-256 Merkle state_root, Ed25519-signed manifest, offline verify, hash-chained AuditLog.3
rustag-replayTime-travel: content-addressed Checkpoint, deterministic Journal replay, Timeline diffs, fork-of-fork Lineage.3
rustag-compressionOff-chain spl-account-compression-compatible ConcurrentMerkleTree (keccak-256, changelog, root-history, canopy).3
packages/sdk@rustag/sdk — TypeScript client for the REST API.1
packages/anchor-plugin@rustag/anchor-plugin — ephemeral stagenet provider for Anchor tests against real mainnet state.2

Phase 2 & 3

Everything beyond Phase 1 is built on a single invariant: a Dirty or Pinned account is never overwritten by any sync — re-enforced on every new path, including the realtime push path.

Phase 2 features Phase 2 · Preview

  • Real-time mirror (push) — a server-side accountSubscribe WebSocket (the Geyser/Yellowstone protocol) updates oracle prices sub-second, behind the realtime feature. A native Yellowstone gRPC source is a drop-in producer for the same mpsc channel.
  • Activity Scheduler — recurring on-chain actions on @every / aliases / 5-field cron (Vixie semantics, no external cron crate); actions are airdrop, signed transfer, or raw-tx replay.
  • Simulation frameworkfork / replay / stress / compare against an isolated in-memory copy; the base is never mutated and mainnet is never touched. Reachable via POST /api/simulate and client.simulate([...]).
  • Analytics — a background sampler captures TVL, transaction volume, accounts mirrored, dirty count, and slot as a queryable time-series.
  • Cloud control plane (rustag-cloud) — multi-tenant orchestration; each stagenet is an isolated child process behind a reverse proxy with Bearer rk_… API-key auth and enforced cross-tenant isolation.
  • GitHub Action & Anchor plugin — an ephemeral per-PR stagenet, and @rustag/anchor-plugin's rustagAnchorProvider({ preload }) / EphemeralStagenet.
stress a fork (rustag-sim) — the base is never mutated
let mut fork = base.fork("herd").await?;let report = rustag_sim::stress(&mut fork, "liquidations", 1_000, |i| build_tx(i)).await?;println!("success rate: {:.1}%", report.success_rate() * 100.0);

Phase 3 features Phase 3 · Experimental

Phase 3 is about trust and depth — making the output of staging something an auditor, grant committee, or CI gate can cryptographically rely on. Every artifact is byte-for-byte reproducible from public inputs.

  • Verifiable attestation (rustag-attest) — a SHA-256 Merkle state_root over the pubkey-sorted account set, an Ed25519-signed manifest, and an offline rustag verify; plus a tamper-evident, hash-chained AuditLog.
  • Time-travel & replay (rustag-replay) — content-addressed Checkpoints, deterministic Journal replay (verify_deterministic), Timeline diffs, and fork-of-fork Lineage with full ancestry.
  • Adversarial simulation (rustag-sim) — atomic Jito-style bundles with tip accounting, deterministic invariant fuzzing (capturing the reproducing seed), a reproducible exploit-signature scanner, and a differential-execution harness.
  • State / ZK compression testing (rustag-compression) — a keccak-256 ConcurrentMerkleTree matching spl-account-compression so compressed-state programs and their proofs verify deterministically off-chain.
build a concurrent Merkle tree and verify a proof (rustag-compression)
use rustag_compression::{ConcurrentMerkleTree, keccak256, verify_path}; let mut tree = ConcurrentMerkleTree::new(14, 64).unwrap();let root = tree.append(keccak256(b"first compressed leaf")).unwrap(); let proof = tree.prove(0).unwrap();assert!(verify_path(&root, &proof.leaf, proof.leaf_index, &proof.siblings));
Honest boundary
Executing arbitrary mainnet programs end-to-end — e.g. a full Jupiter swap — needs the fuller program-loading planned for Phase 2+. Phase 1 loads program accounts verbatim (readable and present) but does not yet JIT-load their BPF bytecode from the program-data account. Your own deployed program reading real mainnet state works today. Real Firedancer execution in the differential harness is a documented trait Backend extension point.