# Template data model Every kickstart and iPXE template is rendered with Go `text/template` against a single flat value. This is the exact, stable contract template authors code against. It is assembled in `internal/render.dataFor` from a NetBox device (`internal/netbox`) plus render-time config (env/Vault). ## Kickstart templates (`*.ks.tmpl`) | Field | Type | Source | Notes | |-------|------|--------|-------| | `.Hostname` | string | NetBox device name | short name, e.g. `web01` | | `.Domain` | string | NetBox CF `domain`, else `BOOTAPI_DOMAIN` | | | `.FQDN` | string | derived | `Hostname.Domain` (or `Hostname` if no domain) | | `.Platform` | string | NetBox platform slug | e.g. `almalinux9` — primary template-selection key | | `.OSFamily` | string | derived from platform | e.g. `almalinux` | | `.OSVersion` | string | derived from platform | e.g. `9` | | `.Arch` | string | fixed `x86_64` (today) | | | `.Role` | string | NetBox device role slug | e.g. `kubernetes-worker` | | `.Interfaces` | `[]Interface` | NetBox interfaces + IPs | primary interface sorted first | | `.PrimaryInterface` | `*Interface` | derived | the NIC carrying the primary IP (or first) | | `.PrimaryIP` | string | NetBox device `primary_ip` | address only, no prefix | | `.Nameservers` | `[]string` | NetBox CF `nameservers`, else `BOOTAPI_NAMESERVERS` | | | `.RootPasswordHash` | string | **render-time** (`BOOTAPI_ROOT_PASSWORD_HASH[_FILE]`) | crypt(3) hash; empty ⇒ lock root. **Never** from NetBox — see [security.md](security.md) | | `.SSHAuthorizedKeys` | `[]string` | **render-time** (`BOOTAPI_SSH_AUTHORIZED_KEYS`) | | | `.PuppetServer` | string | `BOOTAPI_PUPPET_SERVER` | default `puppet.query.consul` | | `.PuppetCAServer` | string | `BOOTAPI_PUPPET_CA_SERVER` | default `puppetca.query.consul` | | `.BaseURL` | string | `BOOTAPI_BASE_URL` | bootapi's own URL | | `.BootBaseURL` | string | `BOOTAPI_BOOT_BASE_URL` | OS install-tree base | | `.KickstartURL` | string | derived | `BaseURL/ks/Hostname` | | `.Custom` | `map[string]any` | **all** NetBox custom fields, verbatim | escape hatch for site-specific knobs without a code change | ### `Interface` | Field | Type | Notes | |-------|------|-------| | `.Name` | string | NetBox interface name, e.g. `eth0` | | `.MAC` | string | normalized lower-case colon form | | `.IP` | string | address only (empty ⇒ no IP; skip in the network stanza) | | `.PrefixLen` | int | CIDR length, e.g. `24` | | `.Netmask` | string | dotted-quad, e.g. `255.255.255.0` | | `.Gateway` | string | per-IP CF `gateway`, else device CF `gateway`, else empty | | `.VLAN` | int | untagged VLAN id, or 0 | | `.Primary` | bool | true for the NIC with the primary IP | ## iPXE templates (`*.ipxe.tmpl`) Rendered with everything above **plus**: | Field | Type | Notes | |-------|------|-------| | `.KernelURL` | string | `BootBaseURL/images/pxeboot/vmlinuz` (empty if `BootBaseURL` unset) | | `.InitrdURL` | string | `BootBaseURL/images/pxeboot/initrd.img` | The fallback templates (`fallback-local`, `fallback-shell`) are rendered with an empty value — they take no host data by design. ## NetBox custom fields bootapi reads Define these on the *device* (or, where noted, the *IP address*) in NetBox. All are optional; sensible fallbacks apply. | Custom field | On | Effect | |--------------|----|--------| | `domain` | device | DNS domain; overrides `BOOTAPI_DOMAIN` | | `gateway` | device / IP address | default gateway (IP-level wins) | | `nameservers` | device | comma-separated resolvers; overrides `BOOTAPI_NAMESERVERS` | | `provision_template` | device | force a specific template name (see below) | ## Template selection precedence `SelectKickstart` picks the first template name that exists, in order: 1. `provision_template` custom field (exact template name) 2. `.Platform` slug (e.g. `almalinux9`) 3. `.OSFamily` (e.g. `almalinux`, or `fedora`) 4. `BOOTAPI_DEFAULT_TEMPLATE` (default `almalinux9`)