{
  "description": "MetalNode is one Mac running the metal-agent, joined to the cluster. The\nadmin creates it (with its ServiceAccount and Lease); the agent reports\nthrough the Lease; the controller fills status and verifies the Mac is\nreachable with a dial-back.",
  "properties": {
    "apiVersion": {
      "description": "APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
      "type": [
        "string",
        "null"
      ]
    },
    "kind": {
      "description": "Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
      "type": [
        "string",
        "null"
      ]
    },
    "metadata": {
      "type": [
        "object",
        "null"
      ]
    },
    "spec": {
      "additionalProperties": false,
      "description": "MetalNodeSpec is written by the cluster admin (llmkube metal token create).\nThe agent has no write access to it.",
      "properties": {
        "hostIP": {
          "description": "HostIP is an address the admin knows reaches this Mac. It is tried\nbefore the agent's own candidates and is still verified by the\ndial-back; it is never trusted on its own.\nThe controller validates it (Reachable=False, reason InvalidSpec, when\nit is not an IP address) rather than a CEL rule, whose isIP() would\nneed Kubernetes 1.31.",
          "maxLength": 45,
          "type": [
            "string",
            "null"
          ]
        },
        "leaseName": {
          "description": "LeaseName is the coordination.k8s.io Lease, in this namespace, that the\nagent renews as its heartbeat and annotates with its report.",
          "maxLength": 253,
          "minLength": 1,
          "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
          "type": "string"
        },
        "serviceAccountName": {
          "description": "ServiceAccountName is the ServiceAccount the Mac's agent authenticates\nas. Deleting that ServiceAccount revokes every token the agent holds.",
          "maxLength": 253,
          "minLength": 1,
          "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
          "type": "string"
        },
        "verifier": {
          "additionalProperties": false,
          "description": "Verifier places the dial-back verifier pod. Point it at the nodes the\nrelay pods for this Mac run on, so the check tests the network path\nthe relay will use.",
          "properties": {
            "nodeSelector": {
              "additionalProperties": {
                "type": "string"
              },
              "description": "NodeSelector is copied to the verifier pod.",
              "type": [
                "object",
                "null"
              ]
            },
            "tolerations": {
              "description": "Tolerations are copied to the verifier pod.",
              "items": {
                "additionalProperties": false,
                "description": "The pod this Toleration is attached to tolerates any taint that matches\nthe triple \u003ckey,value,effect\u003e using the matching operator \u003coperator\u003e.",
                "properties": {
                  "effect": {
                    "description": "Effect indicates the taint effect to match. Empty means match all taint effects.\nWhen specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "key": {
                    "description": "Key is the taint key that the toleration applies to. Empty means match all taint keys.\nIf the key is empty, operator must be Exists; this combination means to match all values and all keys.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "operator": {
                    "description": "Operator represents a key's relationship to the value.\nValid operators are Exists, Equal, Lt, and Gt. Defaults to Equal.\nExists is equivalent to wildcard for value, so that a pod can\ntolerate all taints of a particular category.\nLt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "tolerationSeconds": {
                    "description": "TolerationSeconds represents the period of time the toleration (which must be\nof effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,\nit is not set, which means tolerate the taint forever (do not evict). Zero and\nnegative values will be treated as 0 (evict immediately) by the system.",
                    "format": "int64",
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "value": {
                    "description": "Value is the taint value the toleration matches to.\nIf the operator is Exists, the value should be empty, otherwise just a regular string.",
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "type": "object"
              },
              "type": [
                "array",
                "null"
              ]
            }
          },
          "type": [
            "object",
            "null"
          ]
        }
      },
      "required": [
        "leaseName",
        "serviceAccountName"
      ],
      "type": "object"
    },
    "status": {
      "additionalProperties": false,
      "description": "MetalNodeStatus is written only by the controller.",
      "properties": {
        "agentVersion": {
          "description": "AgentVersion is the version the agent reports.",
          "type": [
            "string",
            "null"
          ]
        },
        "candidates": {
          "description": "Candidates are the addresses tried, in preference order, with the last\ndial-back result for each.",
          "items": {
            "additionalProperties": false,
            "description": "MetalNodeCandidate is one address and what the last dial-back found there.",
            "properties": {
              "address": {
                "description": "Address is an IP address.",
                "type": "string"
              },
              "checkedAt": {
                "description": "CheckedAt is when the verifier finished the check that produced Result.",
                "format": "date-time",
                "type": [
                  "string",
                  "null"
                ]
              },
              "result": {
                "description": "Result is the dial-back outcome.",
                "enum": [
                  "Pending",
                  "Reachable",
                  "Timeout",
                  "Refused",
                  "PinMismatch",
                  "CertificateMismatch",
                  "NotAllowed",
                  "Error"
                ],
                "type": "string"
              },
              "source": {
                "description": "Source says where the address came from: override (spec.hostIP or the\nagent's --host-ip), tailnet, lan or docker-host.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "address",
              "result"
            ],
            "type": "object"
          },
          "maxItems": 17,
          "type": [
            "array",
            "null"
          ],
          "x-kubernetes-list-type": "atomic"
        },
        "capacity": {
          "additionalProperties": false,
          "description": "Capacity is the hardware the agent reports.",
          "properties": {
            "chip": {
              "description": "Chip is the CPU brand string (for example \"Apple M5 Max\").",
              "type": [
                "string",
                "null"
              ]
            },
            "gpuCores": {
              "description": "GPUCores is the GPU core count.",
              "format": "int32",
              "type": [
                "integer",
                "null"
              ]
            },
            "memory": {
              "description": "Memory is MemoryBytes as a quantity (for example 128Gi), for display.",
              "type": [
                "string",
                "null"
              ]
            },
            "memoryBytes": {
              "description": "MemoryBytes is the Mac's unified memory in bytes.",
              "format": "int64",
              "type": [
                "integer",
                "null"
              ]
            },
            "metalVersion": {
              "description": "MetalVersion is the Metal family the GPU supports (for example \"Metal 4\").",
              "type": [
                "string",
                "null"
              ]
            }
          },
          "type": [
            "object",
            "null"
          ]
        },
        "conditions": {
          "description": "Conditions: AgentConnected and Reachable.",
          "items": {
            "additionalProperties": false,
            "description": "Condition contains details for one aspect of the current state of this API Resource.",
            "properties": {
              "lastTransitionTime": {
                "description": "lastTransitionTime is the last time the condition transitioned from one status to another.\nThis should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.",
                "format": "date-time",
                "type": "string"
              },
              "message": {
                "description": "message is a human readable message indicating details about the transition.\nThis may be an empty string.",
                "maxLength": 32768,
                "type": "string"
              },
              "observedGeneration": {
                "description": "observedGeneration represents the .metadata.generation that the condition was set based upon.\nFor instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\nwith respect to the current state of the instance.",
                "format": "int64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "reason": {
                "description": "reason contains a programmatic identifier indicating the reason for the condition's last transition.\nProducers of specific condition types may define expected values and meanings for this field,\nand whether the values are considered a guaranteed API.\nThe value should be a CamelCase string.\nThis field may not be empty.",
                "maxLength": 1024,
                "minLength": 1,
                "pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$",
                "type": "string"
              },
              "status": {
                "description": "status of the condition, one of True, False, Unknown.",
                "enum": [
                  "True",
                  "False",
                  "Unknown"
                ],
                "type": "string"
              },
              "type": {
                "description": "type of condition in CamelCase or in foo.example.com/CamelCase.",
                "maxLength": 316,
                "pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$",
                "type": "string"
              }
            },
            "required": [
              "lastTransitionTime",
              "message",
              "reason",
              "status",
              "type"
            ],
            "type": "object"
          },
          "type": [
            "array",
            "null"
          ],
          "x-kubernetes-list-map-keys": [
            "type"
          ],
          "x-kubernetes-list-type": "map"
        },
        "hostIP": {
          "description": "HostIP is the first candidate the dial-back verified. It is set only\nwhile Reachable is True.",
          "type": [
            "string",
            "null"
          ]
        },
        "ingressPort": {
          "description": "IngressPort is the agent's TLS ingress port.",
          "format": "int32",
          "type": [
            "integer",
            "null"
          ]
        },
        "ingressSPKI": {
          "description": "IngressSPKI is the base64 SHA-256 digest of the agent ingress\ncertificate's public key, as the agent reports it. The dial-back and\nthe relay complete TLS only against this key; the dial-back also\nrequires the certificate the agent reports, which must name this\nMetalNode's UID.",
          "type": [
            "string",
            "null"
          ]
        },
        "lastHeartbeatTime": {
          "description": "LastHeartbeatTime is the Lease's renewTime, accurate to about a\nminute: it advances when AgentConnected changes or when it is more\nthan 60 seconds behind. The Lease holds the exact time.",
          "format": "date-time",
          "type": [
            "string",
            "null"
          ]
        },
        "observedGeneration": {
          "description": "ObservedGeneration is the spec generation this status reflects.",
          "format": "int64",
          "type": [
            "integer",
            "null"
          ]
        },
        "verification": {
          "additionalProperties": false,
          "description": "Verification is the dial-back bookkeeping.",
          "properties": {
            "consecutiveFailures": {
              "description": "ConsecutiveFailures counts checks in a row that verified no address;\nit sets the retry backoff.",
              "format": "int32",
              "type": [
                "integer",
                "null"
              ]
            },
            "inputHash": {
              "description": "InputHash identifies the pin, ingress certificate, port, candidate\nlist and verifier placement the last completed check ran against. A\nchange starts a new check.",
              "type": [
                "string",
                "null"
              ]
            },
            "lastCheckTime": {
              "description": "LastCheckTime is when the last completed check finished.",
              "format": "date-time",
              "type": [
                "string",
                "null"
              ]
            },
            "lastStartTime": {
              "description": "LastStartTime is when the controller last started a verifier pod.\nStarts are at least 30 seconds apart, whatever the Mac reports.",
              "format": "date-time",
              "type": [
                "string",
                "null"
              ]
            },
            "podUID": {
              "description": "PodUID is the UID of the verifier pod the controller last created.\nOnly that pod's results are read.",
              "type": [
                "string",
                "null"
              ]
            }
          },
          "type": [
            "object",
            "null"
          ]
        }
      },
      "type": [
        "object",
        "null"
      ]
    }
  },
  "required": [
    "spec"
  ],
  "type": "object"
}