Skip to content

NodeFacts Data Model

William Smith edited this page Jul 13, 2026 · 1 revision

NodeFacts Data Model

Relevant source files

The following files were used as context for generating this wiki page:

The NodeFacts data model is the primary schema for describing the state, capabilities, and health of individual nodes within an AXIS cluster. It serves as the fundamental unit of information in the Fact Plane (Layer 1) and is the building block for the ClusterSnapshot (Layer 2) used by the placement engine.

Core Data Structures

NodeFacts Struct

The NodeFacts struct captures a comprehensive point-in-time view of a node. This includes static hardware identifiers, dynamic resource utilization, software tool availability, and resident AI models.

Field Type Description
Name string The logical name of the node from nodes.yaml.
Hostname string The actual hostname reported by the OS.
Status NodeStatus Collection status: complete, partial, unreachable, or error.
Resources *Resources Hardware telemetry (CPU, RAM, GPU, Disk).
ResidentModels []ResidentModel Models currently loaded in memory (Ollama, llama.cpp, MLX).
Tools []ToolInfo Discovered binaries and AI runtimes.
Identity *NodeIdentity Stable hardware UUIDs (e.g., /etc/machine-id or IOPlatformUUID).

Sources: internal/models/types.go:151-240, internal/facts/local.go:52-60

Resources and Hardware Telemetry

The Resources struct provides detailed metrics used for scheduling decisions. AXIS differentiates between total physical RAM and "Allocatable" RAM.

Sources: internal/models/types.go:78-107, internal/facts/remote.go:141-243


Memory Calculation Logic

AXIS uses a multi-layered approach to determine how much RAM is actually available for a task. This prevents system instability by enforcing a SystemReserveMB floor.

RAM Hierarchy Diagram

This diagram maps the natural language concepts of "Available Memory" to the specific functions and fields in the AXIS codebase.

"Natural Language to Code Entity Mapping: Memory"

graph TD
    subgraph "Natural Language Space"
        A["Total Physical RAM"]
        B["Protected System Headroom"]
        C["Currently Free RAM"]
        D["AXIS Task Reservations"]
        E["Usable for New Tasks"]
    end

    subgraph "Code Entity Space (internal/models/memory.go)"
        A --- A1["Resources.RAMTotalMB"]
        B --- B1["SystemReserveMBWithConfig()"]
        C --- C1["Resources.RAMFreeMB"]
        D --- D1["NodeFacts.RAMReservedMB"]
        E --- E1["AllocatableRAMMBWithConfig()"]
    end

    A1 --> B1
    A1 & C1 & B1 --> F["ReservableRAMMBWithConfig()"]
    F --> G["Subtract ReservedMB"]
    G --> E1
Loading

Sources: internal/models/memory.go:1-98, internal/models/types.go:136-150

Calculation Rules

  1. System Reserve: Defaults to 1024MB or a per-node override in nodes.yaml internal/models/memory.go:54-66.
  2. Reservable RAM: The minimum of (Total - Reserve) and Free RAM internal/models/memory.go:69-85.
  3. Allocatable RAM: Reservable minus the active commitments in the reservation.Ledger internal/models/memory.go:88-98.

Epistemic State and Discovery

AXIS tracks how it knows what it knows about a node through the NodeStatus and CollectedAt fields.

Node Discovery Flow

This diagram shows how LocalCollector and RemoteCollector populate the NodeFacts model.

"Discovery Data Flow: Collectors to NodeFacts"

graph LR
    subgraph "Collectors (internal/facts/)"
        LC["LocalCollector.Collect()"]
        RC["RemoteCollector.Collect()"]
    end

    subgraph "Discovery Probes"
        P1["uname/sw_vers"]
        P2["/proc/meminfo"]
        P3["nvidia-smi/ioreg"]
        P4["Ollama API"]
    end

    subgraph "Data Model"
        NF["models.NodeFacts"]
    end

    LC & RC --> P1 & P2 & P3 & P4
    P1 -->|"OS/Arch"| NF
    P2 -->|"Resources"| NF
    P3 -->|"GPUs"| NF
    P4 -->|"ResidentModels"| NF
Loading

Sources: internal/facts/local.go:52-143, internal/facts/remote.go:36-139

Resident Models and Warmth

AXIS tracks "Resident Models" to optimize for "warm" starts. If a node already has llama3:70b loaded in its ResidentModels list, the ranker will prioritize it to avoid loading latency internal/facts/local.go:105-124.


The Reservation Ledger

While NodeFacts represents the observed state, the reservation.Ledger tracks intent. This is a double-entry accounting system that prevents race conditions where multiple tasks are scheduled on the same RAM before the OS reports it as "used."

Ledger Entry Structure

Field Description
ID Unique execution ID.
RAMMB Committed memory.
OwnerExecID The task ID responsible for the reservation.
LastHeartbeat Used to reclaim memory if a process crashes without releasing.
ExpiresAt Hard TTL for the reservation.

Sources: internal/reservation/ledger.go:22-35

Overlay Mechanism

The snapshotview.ApplyReservationView function takes a raw ClusterSnapshot and overlays the ledger's active entries. This produces the RAMAllocatableMB field seen by the placement engine, ensuring that "in-flight" tasks are accounted for even if they haven't started consuming physical RSS yet.

Sources: internal/snapshotview/overlay.go:65-114


Failure Records and Health

NodeFacts includes failure metadata to implement the cluster's "immune system."

  • SSHHandshakeLatencyMs: Tracked to detect degraded network paths internal/facts/remote.go:56-59.
  • NodeStatus: If a node is StatusPartial, it indicates that while the node is reachable, specific probes (like GPU telemetry) failed, which may disqualify it from certain workloads internal/models/types.go:12-24.

Sources: internal/models/types.go:12-33, internal/facts/remote.go:33-53


Clone this wiki locally