Architecture¶
Xila Guard is three sibling repositories around one principle: there is a single, canonical, pure policy evaluator, and everything else adapts inputs to it or acts on its outputs.
The single-evaluator principle¶
Policy semantics are implemented exactly once, in Rust (xila-guard-core). The
Go Kubernetes layer and the lab never reimplement policy logic — they build a
normalized request and call the core evaluator (the CLI for offline use, the HTTP
API in-cluster). This keeps decisions consistent across the CLI, the API, the
observer, the scheduler plugin, and the webhook, and makes cross-language drift a
test failure rather than a production surprise.
flowchart LR
subgraph core[xila-guard-core · Rust]
EV[Pure evaluator<br/>evaluate(policy, workload,<br/>candidate, snapshot, ctx)]
end
CLI[CLI xila-guard] --> EV
API[HTTP server<br/>/v1alpha1/evaluate] --> EV
OBS[Observer] --> API
SCH[Scheduler plugin] --> API
WH[Admission webhook] --> API
The pure evaluator performs no network calls, hardware access, clock reads,
or random ID generation. Time and source-acceptance configuration are passed
explicitly through an EvaluationContext. Identical normalized input and context
produce identical semantic results and reason ordering.
The three repositories¶
| Repository | Language | Responsibility |
|---|---|---|
xila-guard-core |
Rust | Canonical contracts + versioned JSON Schemas, the pure evaluator, discovery providers (mock, NVIDIA), CLI, bounded loopback HTTP API, and the opt-in mTLS inventory receiver |
xila-guard-k8s |
Go | CRDs (*.guard.xila.us), the typed core client, tenant identity resolution, the observer + drift, opt-in enforcement (scheduler plugin + fail-closed webhook + RBAC), orchestrator integrations, and the Helm chart |
xila-guard-lab |
bash / make | Reproducible demos, synthetic fixtures, latency benchmark, supply-chain scans, and the cross-repo versions.yaml manifest |
The canonical wire contract is the versioned JSON Schema in core. Go pins a core
contract revision via versions.yaml and validates its structs against the
committed schemas; a drift-detecting test fails CI in either language if they
diverge. Shared request → decision fixtures (15 scenarios) are evaluated in Rust
and decoded in Go to catch semantic drift.
Crate / package layout¶
| Crate | Role |
|---|---|
contracts |
Wire types, strict parsing (reject duplicate / unknown keys), canonical policy digest, JSON-Schema generation |
policy |
The pure, deterministic evaluator — the only implementation of policy semantics |
providers |
DiscoveryProvider + mock; real read-only NVIDIA adapter; opt-in RATS verifier |
cli |
xila-guard (policy validate / evaluate / inventory / attest verify) |
server |
xila-guard-server bounded loopback API + opt-in mTLS inventory receiver |
agent |
xila-guard-agent read-only discovery agent |
| Path | Contents |
|---|---|
api/v1alpha1 |
CRD types + generated deepcopy |
internal/coreclient |
Typed wire structs mirroring the core schemas + HTTP client |
internal/identity |
Tenant resolution from TenantBinding by Namespace UID |
internal/observer |
Request building, core call, JSONL + Event export, drift detection |
internal/admission |
Lab schema/identity handler and the fail-closed enforcement validator + webhook server |
internal/enforce |
Enforce profiles (production vs lab-simulated), env config, request builder |
internal/scheduler |
XilaGuardEnforcement kube-scheduler framework plugin |
internal/integration |
Modular orchestrator profiles (pure data, no vendor imports) |
cmd/{controller,scheduler,webhook} |
Component entrypoints |
charts/xila-guard |
Helm chart |
Data flow 1 — observe (default)¶
The observer watches Pods, policies, tenant bindings, and inventory. For a bound Pod it evaluates the observed node at node scope; for an unbound Pod a candidate evaluation is advisory. It reports what was evaluated and whether device allocation was actually known.
sequenceDiagram
participant Pod as Pod (+ guard.xila.us annotations)
participant CRD as CRDs<br/>PlacementPolicy / TenantBinding / NodeSecurityState
participant OBS as Observer (Go)
participant CORE as Core evaluator (Rust)
participant OUT as JSONL + Kubernetes Events
Pod->>OBS: watched (bound to a node)
CRD->>OBS: policy + tenant (by Namespace UID) + inventory snapshot
OBS->>CORE: POST /v1alpha1/evaluate (normalized request)
CORE-->>OBS: PlacementDecision (allow / deny / indeterminate + reason codes)
OBS->>OUT: decision record + bounded, redacted Event
Note over OBS,OUT: outcome change or lost evidence => drift event (report-only)
Tenant identity is resolved from administrator-owned TenantBinding objects
keyed on the Namespace UID — never a tenant-supplied claim, and never
inherited after a namespace is deleted and recreated.
Data flow 2 — authenticated inventory → evaluate-node¶
With the opt-in SPIFFE/mTLS feature enabled, a per-node collector authenticates to the core inventory receiver over mTLS. The receiver binds each report to the node UID taken from the verified client certificate — never the request body — then evaluation can run against those SVID-bound facts.
sequenceDiagram
participant COL as Per-node collector (SVID)
participant RX as Inventory receiver (core, mTLS)
participant STORE as SVID-bound facts
participant OBS as Observer / enforcement
participant CORE as Evaluator
COL->>RX: POST /v1alpha1/inventory (client SVID)
RX->>RX: bind nodeUid from cert, not body
Note over RX: spoof => 403 · replay => 409 · untrusted => TLS reject
RX->>STORE: store snapshot (source authenticated_collector)
OBS->>CORE: POST /v1alpha1/evaluate-node (fail-closed if absent)
CORE->>STORE: GET /v1alpha1/inventory/{nodeUid}
CORE-->>OBS: decision (enforce accepts authenticated_collector, still rejects mock)
See identity for the trust details and SPIRE wiring.
Execution modes¶
Mode is deployment configuration set by an administrator — never a tenant annotation. A denial reported in observe mode remains a denial; only the integration's action differs.
| Mode | Behavior | Availability |
|---|---|---|
| Offline | Evaluate explicit policy, workload, candidate, and snapshot files | M1 |
| Observe | Evaluate proposed / observed placements and report findings; no scheduling change | M2 |
| Enforce | Block disallowed / unsupported placements at actual integration points | M3 — experimental, narrowly scoped |
Canonical data model (selected types)¶
| Type | Meaning |
|---|---|
WorkloadIdentity |
Cluster ID, namespace UID, workload UID, server-resolved tenant ID, effective policy reference |
PlacementCandidate |
Candidate ID, node UID, scope (node or device), selected device IDs if actually known |
HardwareSnapshot |
Schema version, source binding, snapshot revision, node incarnation, observations, completeness + source errors |
DeviceFacts |
Vendor / model, physical ID, parent relation, driver / firmware observations, sharing dimensions, supported capabilities |
Observation<T> |
Typed value when known, status (known/unknown/unsupported/error), source, observed + received time, source identity, evidence reference |
PlacementDecision |
Outcome, evaluated subject / scope, requirement results, reason codes, policy digest, snapshot references, validity |
DriftEvent |
Previous / current decision IDs, changed facts, detection time, severity, integration action |
Sharing is modeled as separate dimensions (partitioning, time_slicing,
mps, virtualization) — never collapsed into a single sharing_mode, so a
time-sliced MIG resource is representable. Evidence sources are explicit
categories, not a numeric trust score. See the policy model.