Custom resources¶
Xila Guard keeps its Kubernetes API surface deliberately small: three
administrator-owned custom resources in the group guard.xila.us, version
v1alpha1. There is no CRD-per-internal-type; bulky raw evidence stays out of
etcd, and decisions go to JSONL + bounded Kubernetes Events instead of CRD
status spam.
| Kind | Owner | Purpose |
|---|---|---|
PlacementPolicy |
administrator | Cluster policy: explicit requirements + freshness limits |
TenantBinding |
administrator | Maps a Namespace UID to a tenant ID + effective policy |
NodeSecurityState |
controller-written | Normalized inventory summary + evidence status for a node |
All three are cluster-scoped and use apiextensions.k8s.io/v1. Tenants cannot
create or modify any of them — nor the evidence-source configuration (enforced
by RBAC).
PlacementPolicy¶
Administrator-owned cluster policy. The spec.requirements map and freshness
limits are exactly the policy model predicates.
apiVersion: guard.xila.us/v1alpha1
kind: PlacementPolicy
metadata:
name: tenant-dedicated-baseline
spec:
placementScope: node
requirements:
allowedRegions: [eu-central-1]
nodeTenantAssignment: sameTenant
gpu:
timeSlicing: forbidden
mps: forbidden
allowedPartitioning: [none]
approvedDriverVersions: ["fixture-driver-1"]
attestation: notRequired
evidence:
maxAgeSeconds: 60
maxClockSkewSeconds: 5
TenantBinding¶
Maps a Namespace UID to a tenant ID and the effective policy reference. Using the Namespace UID (not the name) avoids accidentally inheriting trust after a namespace is deleted and recreated — a new namespace with the same name gets a new UID and does not inherit the old binding.
apiVersion: guard.xila.us/v1alpha1
kind: TenantBinding
metadata:
name: team-alpha
spec:
namespaceUid: "<Namespace .metadata.uid>"
tenantId: team-alpha
effectivePolicyRef: tenant-dedicated-baseline
The authoritative effectivePolicyRef wins over the guard.xila.us/policy
Pod annotation. Tenant identity is always server-resolved from this object —
never from a tenant-supplied claim.
NodeSecurityState¶
Written by the controller: a normalized inventory summary and evidence status for
a node, keyed by its stable node UID. In the lab this carries small synthetic
mock snapshots; with the opt-in identity feature it can reflect SVID-bound
authenticated_collector inventory.
apiVersion: guard.xila.us/v1alpha1
kind: NodeSecurityState
metadata:
name: node-a
spec:
clusterId: xila-guard-lab
nodeUid: "<Node .metadata.uid>"
snapshotRevision: 7
source: mock # mock | admin_inventory | host_observation | authenticated_collector
observedTime: "2026-10-10T00:00:00Z"
devices:
- physicalId: GPU-...
model: ...
partitioning: none
timeSlicing: { status: unknown }
mps: { status: unknown }
Freshness matters
NodeSecurityState snapshots are evaluated against the controller clock. Lab
fixtures render a fresh observedTime at apply time so mock evidence stays
within the policy's maxAgeSeconds (default 60); a stale snapshot yields
EVIDENCE_STALE → indeterminate, never allow.
Installing the CRDs¶
The CRDs ship with the Helm chart (installCRDs: true by default) and
are also available as raw manifests under config/crd/bases in xila-guard-k8s.
The generated schemas come from controller-tools (controller-gen v0.19).