Schema
In Infrahub, the schema is at the center of most things and our goal is to provide as much flexibility as possible to allow users to extend and customize the schema.
Out of the box, Infrahub doesn't have a schema for most things and it's up to users to load a schema that fits their needs. Over time we plan to maintain different schemas for the common types of use cases, but for now, we are providing one example schema to model a basic network with objects like Device, Interface, IPAddress, etc.
Unlike traditional databases that can only have one schema at a time, in Infrahub it is possible to have a different schema per branch. This is possible because the schema itself is stored in the database like any other object.
There are several ways to load a new schema.
To help with the development process of a schema definition file, you can leverage the schemas we publish for validation within your editor
Exploring the Infrahub Schema video
A recording of a livestream that covers:
- What the Infrahub schema actually is
- Why we built it this way (and what we rejected)
- How it compares to other source of truth tools
- Best practices for customizing the schema to your environment
Schema definition
Node, Attributes, Relationships, and Generics
The schema is composed of 4 primary types of objects: Nodes- that are themselves composed of Attributes and Relationships and finally Generics.
- A Nodein Infrahub represents aModel. Nodes are instances of a specific object within your infrastructure. Nodes have attributes that define specific values, such as text or numbers, and relationships that link them to other nodes.
- An Attributerepresents a direct value associated with aNodelike aText, aNumberetc ...
- A Relationshiprepresents a link between 2Node, aRelationshipcan be of cardinalityoneormany.
- A Genericcan be used to share attributes and relationships between different types ofNodes. They can connect multiple types of nodes to the same relationship or define attributes and relationships on a specific list of nodes. Generics are similar to class inheritance in programming languages like Java or Python.
Some namespace like Core, Infrahub, Profile, and Builtin are reserved and can't be used by users to defined new Nodes and Generics.
In a similar fashion, some attributes name can't be used, like attribute or relationship.
Nodes vs. Generics
Use a Node when you need to represent a concrete object in your infrastructure model with specific attributes and relationships.
Use a Generic when you want to share common attributes or relationships across multiple node types. This helps to avoid redundancy and ensures consistency across your schema. For example, if you have different types of network interfaces (physical, logical) that share common attributes like name and description, you can define a Generic interface with these attributes and have the specific interface types inherit from it.
Generics can also be used to connect multiple types of Nodes to the same relationship.
A Generic cannot exist independently; it must be instantiated by at least one Node.
Two limitations could have an impact on your choice of node vs generic :
- computed attributes can only be used on Nodes, see computed attribute topic for more information.
- Generic's properties are propagated to Nodes only during the first import see the section Inherited properties below.
Field ordering with order weight
The order in which attributes and relationships appear in the Infrahub frontend is controlled by the order_weight property. Items with lower values appear before those with higher values. When not explicitly specified, Infrahub automatically assigns default values in increments of 1000:
nodes:
  - name: Device
    namespace: Example
    attributes:
      - name: hostname        # Gets order_weight: 1000
        kind: Text
      - name: description     # Gets order_weight: 2000
        kind: Text
        optional: true
      - name: serial_number   # Gets order_weight: 3000
        kind: Text
    relationships:
      - name: location        # Gets order_weight: 4000
        peer: ExampleLocation
        cardinality: one
This automatic assignment ensures predictable field ordering while leaving room for custom positioning. To customize field order, you can specify explicit order_weight values:
attributes:
  - name: asset_tag
    kind: Text
    order_weight: 100       # Appears first
  - name: hostname
    kind: Text
    order_weight: 200       # Appears second
  - name: description
    kind: Text
    optional: true
    order_weight: 9000      # Appears last
Node example
In the example below, the node Person has 2 attributes (name and description) and the node Car has 1 attribute (model) and 1 relationship to Person, identified by owner.
nodes:
  - name: Person
    namespace: Example1
    attributes:
      - name: name
        kind: Text
        unique: true
      - name: description
        kind: Text
        optional: true
  - name: Car
    namespace: Example1
    attributes:
      - name: model
        kind: Text
    relationships:
      - name: owner
        peer: Example1Person
        optional: false
        cardinality: one
        kind: Attribute
Node, Attribute, and Relationship are defined by their kind. While the name and the namespace of the node are up to the creator of the schema, the kinds for the attributes and the relationships are coming from Infrahub. The kind of an attribute, or a relationship, is important because it defines how each element is represented in GraphQL and the UI.
The kind of a model is generated by concatenating the namespace and the name.
Attribute kinds
- Text: Standard Text
- Number: Standard Number
- NumberPool: Standard Number that creates a- CoreNumberPoolresource manager tied to the attribute
- TextArea: Long-form Text that can span multiple lines
- DateTime: A Date and a Time
- Dropdown: A list of choices, each choice can have a color and description
- Email: Email address
- Password: A Text String that should be obfuscated but which can be read with enough right in the UI
- HashedPassword: Similar to Password but it will not be shown in the UI and can't be re-read.
- URL: An URL to a website or a resource over HTTP
- File: Path to a file on the filesystem
- MacAddress: Mac Address following the format (XX:XX:XX:XX:XX:XX)
- Color: An HTML color
- Boolean: Flag that can be either True or False
- Bandwidth: Bandwidth in kbps
- IPHost: IP Address in either IPV4 or IPv6 format
- IPNetwork: IP Network in either IPV4 or IPv6 format
- Checkbox: Duplicate of- Boolean
- List: List of any value
- JSON: Any data structure compatible with JSON
- Any: Can be anything
Attribute kinds behavior in the UI
| Kind | Display in List View | Display in Detailed View | 
|---|---|---|
| ID | No | Yes | 
| Text | Yes | Yes | 
| Number | Yes | Yes | 
| NumberPool | Yes | Yes | 
| Boolean | Yes | Yes | 
| Dropdown | Yes | Yes | 
| TextArea | No | Yes | 
| DateTime | No | Yes | 
| Email | Yes | Yes | 
| Password | No | Yes | 
| URL | Yes | Yes | 
| File | Yes | Yes | 
| MacAddress | Yes | Yes | 
| Color | Yes | Yes | 
| Bandwidth | Yes | Yes | 
| IPHost | Yes | Yes | 
| IPNetwork | Yes | Yes | 
| Checkbox | No | Yes | 
| List | No | Yes | 
| JSON | No | Yes | 
| Any | No | Yes | 
Attribute parameters
There are some attribute kinds that allow optional parameters to be defined to control the behavior of the attribute. Below are the attribute kinds and their accepted parameters.
- Number
- NumberPool
- Text
- TextArea
| Parameter | Default | 
|---|---|
| min_value | None | 
| max_value | None | 
| excluded_values | None | 
| Parameter | Default | 
|---|---|
| end_range | 9223372036854775807 | 
| start_range | 1 | 
| Parameter | Default | 
|---|---|
| regex | None | 
| min_length | None | 
| max_length | None | 
| Parameter | Default | 
|---|---|
| regex | None | 
| min_length | None | 
| max_length | None | 
Relationship kinds
- Generic: A flexible relationship with no specific functional significance. It is commonly used when an entity doesn't fit into specialized categories like- Componentor- Parent.
- Attribute: A relationship where related entities' attributes appear directly in the detailed view and list views. It's used for linking key information, like statuses or roles.
- Component: This relationship indicates that one entity is part of another and appears in a separate tab in the detailed view of a node in the UI. It represents a composition-like relationship where one node is a component of the current node.
- Parent: This relationship defines a hierarchical link, with the parent entity often serving as a container or owner of another node. Parent relationships are mandatory and allow filtering in the UI, such as showing all components for a given parent.
- Group(system-managed): Defines a relationship where a node (inheriting from- CoreNode) is a member or subscriber to a group (inheriting from- CoreGroup). Do not model this in user schemas; it's created/managed by Infrahub. These relationships appear in the "Manage Groups" form and enable organizational concepts fundamental to Infrahub's architecture.
- Profile: A special relationship where a node is assigned a profile (inheriting from- CoreProfile), visible during creation or updates through a "select profile" dropdown.
Relationship kinds behavior in the UI
| ID | cardinality | Display in List View | Display in Detailed View | Display in Tab | 
|---|---|---|---|---|
| Generic | one | No | Yes | No | 
| Generic | many | No | No | Yes | 
| Attribute | one | Yes | Yes | No | 
| Attribute | many | Yes | Yes | No | 
| Component | one | No | Yes | No | 
| Component | many | No | No | Yes | 
| Hierarchical Parent | one | Yes | Yes | No | 
| Hierarchical  Children | many | No | No | Yes | 
| Parent | one | No | Yes | No | 
Component and Parent typically belong together:
The Component relationship is typically paired with the Parent relationship.
This ensures a strong relationship in both directions, where the parent node can manage its components, and the component refers back to its parent.
Relationships of kind Component include an implicit on_delete: cascade. This means that if you delete a node with a Component relationship, the related nodes connected by this relationship will also be deleted.
Group and Profile are internal relationship:
The Group and Profile relationship kinds are internal types and should not be directly used by the user in their schema.
These are automatically handled by the system for managing memberships and configurations.
Relationships of kind Component will automatically be covered by the template when enabling template generation on a given schema node. See the object-template topic for more information.
To help you understand the relationship types better, here’s an example schema using a real-world model of Car, Person and Wheel.
version: "1.0"
nodes:
  - name: Car
    namespace: Auto
    description: "A vehicle used for transportation."
    attributes:
      - name: model
        kind: Text
        description: "The model of the car."
      - name: year
        kind: Number
        description: "The manufacturing year of the car."
      - name: license_plate
        kind: Text
        unique: true
        description: "License plate number."
    relationships:
      - name: owner
        peer: AutoPerson
        kind: Attribute
        cardinality: one
        optional: false
      - name: wheels
        peer: AutoWheel
        kind: Component
        cardinality: many
  - name: Wheel
    namespace: Auto
    description: "A wheel of the car, a critical component for movement."
    attributes:
      - name: wheel_size
        kind: Number
        description: "Size of the wheel in inches."
      - name: type
        kind: Text
        description: "Type of the wheel (e.g., alloy, steel)."
    relationships:
      # A wheel must belong to a car, hence the Parent relationship is mandatory
      - name: car
        peer: AutoCar
        kind: Parent
        cardinality: one
        optional: false
  - name: Person
    namespace: Auto
    description: "A person who may own a car."
    attributes:
      - name: first_name
        kind: Text
        description: "First name of the person."
      - name: last_name
        kind: Text
        description: "Last name of the person."
      - name: driver_license_number
        kind: Text
        unique: true
        description: "Driver's license number."
    relationships:
      - name: cars
        peer: AutoCar
        kind: Component
        cardinality: many
        optional: true
- A Carnode might have anownerattribute linking it to aPerson. This relationship will be visible in the car's detailed view.
- A Wheelis a component of aCar, meaning wheels are an essential part of the car. The wheels will be displayed in a separate "Components" tab.
- A Carcan have aProfileCarselected during its creation or update. The insurance details appear in the form where you can pick or assign a profile for the car.