Skip to content

Void Toolkit Architecture Design #106

@freesig

Description

@freesig

Void Toolkit Architecture Design

High-level architecture for the new void-toolkit - a framework for building verifiable applications that derive state from void blocks and generate ZK proofs of state transitions.

Core Philosophy & Goals

The void-toolkit is designed around one principle: minimize what Claude Code needs to generate while maximizing what it can build.

Design Goals:

  1. Obvious patterns - An app developer (human or AI) should look at the API and immediately know how to use it. No hidden macros, no fragmented knowledge across repos.

  2. Type-driven state - Developers think in types, not key-value pairs. A proc macro translates typed state definitions into merkle-compatible storage.

  3. Linear pipeline - Blocks → Events → State Transition → Proof. No over-engineered stream combinators. Just an async loop.

  4. Runtime flexibility - Same code runs as prover or verifier. Storage backend chosen at startup. Indexer toggled on/off.

  5. Single import - use void_toolkit::prelude::* gives you everything. Feature flags control what's compiled.

What the toolkit handles:

  • Event fetching and caching from blockchains
  • P2P networking for blocks and proofs
  • Storage abstraction (memory, persistent, database)
  • ZK proof generation
  • API server for state queries
  • Indexer infrastructure

What the app developer provides:

  • Event type definitions
  • State schema (via #[derive(VoidState)])
  • State transition function
  • Indexer logic (optional)
  • Custom API handlers (optional)

Crate Structure

void-toolkit/
├── crates/
│   ├── types/              # Shared types (blocks, events, hashes, proofs)
│   ├── state/              # State trait, storage backends, proc macros
│   ├── events/             # Event fetcher, blockchain clients, cache
│   ├── p2p/                # P2P networking for blocks and proofs
│   ├── prove/              # ZK proof generation (RISC Zero integration)
│   ├── api/                # HTTP/WebSocket API server for state queries
│   ├── indexer/            # Indexer infrastructure for auxiliary state
│   ├── node/               # Node runtime that ties everything together
│   └── toolkit/            # Facade crate with re-exports
├── skills/
│   ├── creating-void-app/
│   ├── defining-state-schema/
│   ├── writing-stf/
│   ├── adding-indexer/
│   ├── exposing-api/
│   ├── testing-void-apps/
│   ├── running-node/
│   └── deploying-node/
└── examples/
    ├── counter/            # Minimal: single state value, one event type
    ├── token/              # Basic: balances map, transfer events
    ├── auction/            # Intermediate: time-based logic, multiple event types
    └── orderbook/          # Complex: full matching engine, indexer, custom API

Facade crate (toolkit/):

[package]
name = "void-toolkit"

[features]
default = ["node"]
node = ["void-node"]
prover = ["void-prove"]
indexer = ["void-indexer"]
full = ["node", "prover", "indexer"]

Data Flow

flowchart TB
    subgraph External
        BC[Blockchain Contracts]
        BP[Block Producer]
        PN[Peer Nodes]
    end

    subgraph EventFetcher["events/ crate"]
        EF[Event Fetcher]
        EC[Event Cache]
    end

    subgraph P2P["p2p/ crate"]
        BR[Block Receiver]
        PR[Proof Receiver]
        PB[Proof Broadcaster]
    end

    subgraph Node["node/ crate"]
        BL[Block Loop]
        STF[State Transition Function]
    end

    subgraph State["state/ crate"]
        PS[Proven State]
        SB[(Storage Backend)]
    end

    subgraph Prove["prove/ crate"]
        PG[Proof Generator]
        ZK[RISC Zero Guest]
    end

    subgraph Indexer["indexer/ crate"]
        IL[Indexer Logic]
        IS[(Indexed State)]
    end

    subgraph API["api/ crate"]
        AS[API Server]
    end

    BC -->|fetch events| EF
    EF -->|store| EC
    BP -->|blocks with hashes| BR
    PN -->|blocks & proofs| BR
    PN -->|proofs| PR

    BR -->|block| BL
    EC -->|resolve hashes| BL
    BL -->|block + events| STF
    STF -->|read/write| PS
    PS -->|persist| SB

    STF -->|witness| PG
    PG -->|execute STF| ZK
    ZK -->|proof| PB
    PB -->|broadcast| PN

    PR -->|verify| PS

    STF -->|events + state| IL
    IL -->|store| IS

    PS -->|query| AS
    IS -->|query| AS
Loading

The core loop:

  1. Block arrives via P2P (contains event hashes only)
  2. Event hashes resolved against cache (events fetched from blockchains)
  3. State transition function executes with events
  4. Proven state updated
  5. If prover mode: generate ZK proof, broadcast
  6. If indexer enabled: update indexed state
  7. API serves queries against both state types

Component Overview

Crate Purpose
types/ Shared types (blocks, events, hashes, proofs)
events/ Fetch and cache events from blockchains by hash
state/ Type-driven state storage with proc macros
p2p/ Block and proof gossip between nodes
prove/ ZK proof generation via RISC Zero
api/ HTTP/WebSocket server for state queries
indexer/ Auxiliary state derived from events + proven state
node/ Runtime that orchestrates all components
toolkit/ Facade with re-exports and feature flags

Key design notes:

  • types/: Blocks contain event hashes only, not event data. Events resolved separately via events crate.
  • state/: Storage is trait-based and runtime-selectable (memory, persistent, database).
  • indexer/: Indexed state is plain Rust types, not proven. Derives from events and proven state.

Component interaction:

graph LR
    subgraph "External"
        Chains[Blockchains]
        Peers[Peer Nodes]
    end

    subgraph "void-toolkit"
        events --> node
        p2p --> node
        node --> state
        node --> prove
        node --> indexer
        state --> api
        indexer --> api
        prove --> p2p
    end

    Chains --> events
    Peers <--> p2p
Loading

Node Operating Modes

A void-toolkit node can run in different configurations based on feature flags and runtime config:

flowchart TB
    subgraph "Prover Node"
        direction TB
        P_blocks[Receive Blocks] --> P_stf[Run STF]
        P_stf --> P_state[Update State]
        P_stf --> P_prove[Generate Proof]
        P_prove --> P_broadcast[Broadcast Proof]
    end

    subgraph "Verifier Node"
        direction TB
        V_blocks[Receive Blocks] --> V_stf[Run STF]
        V_proofs[Receive Proofs] --> V_verify[Verify Proof]
        V_stf --> V_state[Update State]
        V_verify --> V_state
    end

    subgraph "Optional: Indexer"
        direction TB
        I_events[Events + State] --> I_derive[Derive Indexed State]
        I_derive --> I_store[Store Indexed State]
    end

    subgraph "Optional: API"
        direction TB
        A_proven[Proven State] --> A_serve[Serve Queries]
        A_indexed[Indexed State] --> A_serve
    end
Loading

Mode combinations:

Mode Proves Verifies Indexes API
Full Prover -
Light Prover - - -
Full Verifier -
Light Verifier - - -

All modes run the state transition function. The difference is whether they generate proofs, and which optional components are enabled.


App Developer Interface

What the app developer provides vs what the toolkit handles:

flowchart LR
    subgraph "App Developer Provides"
        Events[Event Types]
        Schema[State Schema]
        STF[State Transition Function]
        Idx[Indexer Logic]
        Handlers[API Handlers]
    end

    subgraph "Toolkit Generates/Provides"
        Decode[Event Decoding]
        Storage[Storage Layer]
        Merkle[Merkle Integration]
        Guest[ZK Guest Program]
        Server[API Server]
        Runtime[Node Runtime]
    end

    Events --> Decode
    Schema --> Storage
    Schema --> Merkle
    STF --> Guest
    Idx --> Runtime
    Handlers --> Server
Loading

App structure:

my-void-app/
├── src/
│   ├── events.rs      # Event type definitions
│   ├── state.rs       # State schema (derive macro)
│   ├── stf.rs         # State transition function
│   ├── indexer.rs     # Optional: indexed state logic
│   └── api.rs         # Optional: custom query handlers
├── Cargo.toml
└── config/
    └── node.toml      # Node runner configuration

Developer workflow:

  1. Define event types that match blockchain contracts
  2. Define state schema using #[derive(VoidState)]
  3. Write state transition function that processes events
  4. Optionally add indexer logic and custom API handlers
  5. Build and run node with desired mode

Skills for Claude Code

Skills guide Claude Code through common tasks when building void apps:

flowchart TB
    subgraph "App Development"
        S1[creating-void-app]
        S2[defining-state-schema]
        S3[writing-stf]
        S4[adding-indexer]
        S5[exposing-api]
        S6[testing-void-apps]
    end

    subgraph "Operations"
        S7[running-node]
        S8[deploying-node]
    end

    S1 --> S2 --> S3
    S3 --> S4
    S3 --> S5
    S3 --> S6
    S6 --> S7 --> S8
Loading

Skill purposes:

Skill When to use
creating-void-app Scaffold new project with correct structure
defining-state-schema Design state using derive macros correctly
writing-stf Patterns for processing events and updating state
adding-indexer When and how to derive auxiliary state
exposing-api Add custom query endpoints
testing-void-apps Test STF with mock blocks and events
running-node Start node locally in different modes
deploying-node Deploy to production infrastructure

Each skill contains step-by-step guidance, common patterns, and pitfalls to avoid.


Architecture Summary

flowchart TB
    subgraph "Blockchains"
        ETH[Ethereum]
        SOL[Solana]
        BASE[Base]
    end

    subgraph "void-toolkit"
        subgraph "Data Ingestion"
            events[events/]
            p2p[p2p/]
        end

        subgraph "Core Runtime"
            node[node/]
            state[state/]
            types[types/]
        end

        subgraph "Optional Components"
            prove[prove/]
            indexer[indexer/]
            api[api/]
        end

        toolkit[toolkit/ - facade]
    end

    subgraph "App Code"
        APP[State Schema + STF + Indexer + API Handlers]
    end

    subgraph "Consumers"
        Peers[Peer Nodes]
        Clients[API Clients]
    end

    ETH & SOL & BASE --> events
    Peers <--> p2p

    events --> node
    p2p --> node
    types --> node
    APP --> node

    node --> state
    node --> prove
    node --> indexer

    state --> api
    indexer --> api
    prove --> p2p

    api --> Clients

    toolkit -.-> events & p2p & node & state & prove & indexer & api
Loading

Key architectural principles:

  1. Separation of concerns - Each crate has one job
  2. Blocks carry hashes, not data - Events resolved separately
  3. All nodes run STF - Prover/verifier distinction is only about proof generation
  4. Type-driven state - Developers define types, toolkit handles storage
  5. Optional components - Indexer, prover, API toggled via features
  6. Single facade - void-toolkit re-exports everything needed

Next Steps

This document serves as guidance for detailed design documents for each component:

  1. types/ - Define exact type structures
  2. state/ - Design the proc macro and storage trait
  3. events/ - Design blockchain clients and cache
  4. p2p/ - Design networking protocol
  5. prove/ - Design RISC Zero integration
  6. api/ - Design server and query interface
  7. indexer/ - Design indexer infrastructure
  8. node/ - Design runtime orchestration
  9. Skills - Write each skill with patterns and examples

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions