Skip to content

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&#40;policy, workload,<br/>candidate, snapshot, ctx&#41;]
    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.