Core Components

Deep dive into AvalancheGo's package structure, startup flow, and how components interact.

This page provides a detailed overview of AvalancheGo's internal architecture, including the main packages, startup sequence, and how components communicate.

Package Structure

AvalancheGo is organized into well-defined packages, each responsible for specific functionality (top-level folders only):

avalanchego/
├── main/            # CLI entry point
├── app/             # Process lifecycle (signals, shutdown)
├── config/          # Flags/env/config parsing
├── node/            # Node wiring and initialization
├── chains/          # Chain manager and handlers
├── snow/            # Consensus protocols and engines
├── vms/             # Built-in VMs, proposerVM, rpcchainvm
├── network/         # P2P stack
├── message/         # Message codecs
├── database/        # LevelDB/Pebble/memdb backends
├── graft/           # Grafted Coreth (C-Chain EVM)
├── subnets/         # Subnet configs and validator utilities
├── staking/         # TLS/BLS staking keys and POP
├── upgrade/         # Network upgrade rules
├── trace/           # OpenTelemetry tracing helpers
├── utils/           # Common utilities
└── genesis/         # Genesis configuration and samples

Startup Flow

When you run AvalancheGo, the following initialization sequence occurs:

1. Configuration Parsing

View source on GitHub

main/main.go
func main() {
    evm.RegisterAllLibEVMExtras()

    // Build configuration from flags/env/config file
    fs := config.BuildFlagSet()
    v, err := config.BuildViper(fs, os.Args[1:])

    if errors.Is(err, pflag.ErrHelp) {
        os.Exit(0)
    }

    if v.GetBool(config.VersionJSONKey) && v.GetBool(config.VersionKey) {
        fmt.Println("can't print both JSON and human readable versions")
        os.Exit(1)
    }

    if v.GetBool(config.VersionJSONKey) {
        versions := version.GetVersions()
        jsonBytes, err := json.MarshalIndent(versions, "", "  ")
        if err != nil {
            fmt.Printf("couldn't marshal versions: %s\n", err)
            os.Exit(1)
        }
        fmt.Println(string(jsonBytes))
        os.Exit(0)
    }

    if v.GetBool(config.VersionKey) {
        fmt.Println(version.GetVersions().String())
        os.Exit(0)
    }

    nodeConfig, err := config.GetNodeConfig(v)
    if term.IsTerminal(int(os.Stdout.Fd())) {
        fmt.Println(app.Header)
    }

    nodeApp, err := app.New(nodeConfig)
    exitCode := app.Run(nodeApp)
    os.Exit(exitCode)
}

The configuration system supports:

  • Command-line flags: --network-id=fuji, --http-port=9650
  • Config file: Pass --config-file=/path/to/file. The installer writes ~/.avalanchego/configs/node.json; source builds do not create a default file.
  • Environment variables: Prefixed with AVAGO_

2. Node Initialization

The Node struct in node/node.go orchestrates all components:

node/node.go
type Node struct {
    Log        logging.Logger
    ID         ids.NodeID
    Config     *node.Config

    // Networking & routing
    Net         network.Network
    chainRouter router.Router
    msgCreator  message.Creator

    // Storage & shared state
    DB           database.Database
    sharedMemory *atomic.Memory

    // VM/chain orchestration
    VMAliaser    ids.Aliaser
    VMManager    vms.Manager
    VMRegistry   registry.VMRegistry
    chainManager chains.Manager

    // APIs and services
    APIServer       server.Server
    health          health.Health
    resourceManager resource.Manager
}

3. Component Initialization Order

Components are initialized in a specific order to satisfy dependencies:

OrderComponentPurpose
1Identity & loggingStaking certs/POP, VM aliases, log factories
2MetricsPrometheus registries + /ext/metrics
3APIsHTTP server + metrics API (health/info/admin added later)
4Database & shared memoryOpen LevelDB/PebbleDB/memdb and atomic memory
5Message codecmessage.Creator shared by network/engines
6Validators & resourcesValidator manager, CPU/disk targeters, resource manager
7NetworkingListener, NAT/port mapping, throttlers, IP updater
8Health & aliasesHealth API, default VM/API/chain aliases
9Chain manager & VM registryChain manager, register PlatformVM/AVM/EVM + plugins
10Indexer & profilerOptional index API and continuous profiler
11ChainsStart PlatformVM, then other chains/bootstrap

The Node Struct

The Node struct is the central coordinator. Here are its key responsibilities:

VM Management

// Register built-in VMs
n.VMManager.RegisterFactory(ctx, constants.PlatformVMID, &platformvm.Factory{})
n.VMManager.RegisterFactory(ctx, constants.AVMID, &avm.Factory{})
n.VMManager.RegisterFactory(ctx, constants.EVMID, &coreth.Factory{})

Chain Creation

When a new chain needs to be created (e.g., during P-Chain bootstrap):

type ChainParameters struct {
    ID          ids.ID      // Chain ID
    SubnetID    ids.ID      // Subnet that validates this chain
    GenesisData []byte      // Genesis state
    VMID        ids.ID      // Which VM to run
    FxIDs       []ids.ID    // Feature extensions
    CustomBeacons validators.Manager // Optional: bootstrap peers for P-Chain
}

API Registration

Each VM can register its own API endpoints:

// VM implements CreateHandlers
func (vm *VM) CreateHandlers(ctx context.Context) (map[string]http.Handler, error) {
    return map[string]http.Handler{
        "/rpc": vm.rpcHandler,
        "/ws":  vm.wsHandler,
    }, nil
}

Chain Manager

The Chain Manager (chains/manager.go) is responsible for:

  1. Creating chains when requested by the P-Chain
  2. Managing chain lifecycle (start, stop, restart)
  3. Handling bootstrapping and state sync
  4. Routing messages between chains and the network
chains/manager.go
type Manager interface {
    // Queue a chain to be created after P-Chain bootstraps
    QueueChainCreation(ChainParameters)

    // Check if a chain has finished bootstrapping
    IsBootstrapped(ids.ID) bool

    // Resolve chain aliases
    Lookup(string) (ids.ID, error)

    // Start the chain creation process
    StartChainCreator(platformChain ChainParameters) error
}

Chain Bootstrapping

When a chain starts, it progresses through several states to catch up with the network:

Database Layer

AvalancheGo supports multiple database backends:

Database Backends

BackendDescription
LevelDBDefault, widely tested
PebbleDBModern alternative, better performance
memdbIn-memory (non-persistent), useful for fast testing

Database Organization

Data is organized using prefix databases:

// Each component gets its own namespace
vmDB := prefixdb.New(VMDBPrefix, db)
chainDB := prefixdb.New(chainID[:], vmDB)

This allows:

  • Isolation: Each VM and chain has isolated storage
  • Metrics: Per-database metrics via meterdb
  • Cleanup: Easy removal of chain data

Message Flow

Here's how a transaction flows through the system:

Health Checks

AvalancheGo exposes health checks at /ext/health:

type Checker interface {
    // HealthCheck returns nil if healthy
    HealthCheck(context.Context) (interface{}, error)
}

Components that implement health checks:

  • Network: Peer connectivity
  • Chains: Bootstrap status
  • Database: I/O health
  • Consensus: Liveness

Metrics

Prometheus metrics are exposed at /ext/metrics:

// Example metrics namespaces
const (
    networkNamespace = "avalanche_network"
    dbNamespace      = "avalanche_db"
    consensusNS      = "avalanche_snowman"
)

Key metrics include:

  • avalanche_network_peers: Connected peer count
  • avalanche_db_*: Database operations
  • avalanche_snowman_*: Consensus metrics
  • avalanche_api_*: API request metrics

Next Steps

Is this guide helpful?