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
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:
- Pre-load — extract the transaction's static account keys and batch-fetch any that are not already loaded and not
Dirtyfrom mainnet via the mirror, loading them into LiteSVM asClean(fetch failures are logged and tolerated). - Execute through LiteSVM with signature and blockhash checks on.
- Track writes — derive writable accounts from the message header layout, mark them
Dirty, and persist their post-state. - 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.
| Crate | Responsibility | Phase |
|---|---|---|
| rustag-core | The runtime: LiteSVM + AccountSync state machine + SQLite persistence + lazy-mirror engine. Central type Stagenet. | 1 |
| rustag-mirror | Mainnet fetcher: raw JSON-RPC over reqwest, known-program/oracle registry, RpcRateLimiter, and the realtime push source. | 1 · realtime 2 |
| rustag-rpc | Solana-compatible JSON-RPC + WebSocket + REST API, all on axum (serve, ServerAddrs, AppState). | 1 |
| rustag-cli | The rustag binary (clap subcommands for every Phase 1/2/3 command). | 1 + 2/3 |
| rustag-scheduler | Activity Scheduler: pairs a Schedule (@every / cron) with an Action (airdrop / transfer / raw-tx). | 2 |
| rustag-sim | Simulation: fork/replay/stress/compare, MEV/Jito bundles, invariant fuzzing, exploit scanning, differential execution. | 2 / 3 |
| rustag-cloud | Multi-tenant control plane: each stagenet an isolated child process behind a reverse proxy with API-key auth. | 2 |
| rustag-attest | Verifiable attestation: SHA-256 Merkle state_root, Ed25519-signed manifest, offline verify, hash-chained AuditLog. | 3 |
| rustag-replay | Time-travel: content-addressed Checkpoint, deterministic Journal replay, Timeline diffs, fork-of-fork Lineage. | 3 |
| rustag-compression | Off-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
accountSubscribeWebSocket (the Geyser/Yellowstone protocol) updates oracle prices sub-second, behind therealtimefeature. A native Yellowstone gRPC source is a drop-in producer for the samempscchannel. - 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 framework —
fork/replay/stress/compareagainst an isolated in-memory copy; the base is never mutated and mainnet is never touched. Reachable viaPOST /api/simulateandclient.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 withBearer rk_…API-key auth and enforced cross-tenant isolation. - GitHub Action & Anchor plugin — an ephemeral per-PR stagenet, and
@rustag/anchor-plugin'srustagAnchorProvider({ preload })/EphemeralStagenet.
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 Merklestate_rootover the pubkey-sorted account set, an Ed25519-signed manifest, and an offlinerustag verify; plus a tamper-evident, hash-chainedAuditLog. - Time-travel & replay (
rustag-replay) — content-addressedCheckpoints, deterministicJournalreplay (verify_deterministic),Timelinediffs, and fork-of-forkLineagewith 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-256ConcurrentMerkleTreematchingspl-account-compressionso compressed-state programs and their proofs verify deterministically off-chain.
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));trait Backend extension point.