> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cnap.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Cross-Cluster Comparison

> Compare Kubernetes versions, images, and configurations across multiple clusters in one call

When managing multiple clusters, drift happens — different Kubernetes versions, mismatched images, inconsistent configurations. An agent can audit all clusters in parallel, comparing everything from node versions to installed CRDs.

## Version Drift Check

An agent asked "are my clusters running the same versions?" fans out across all clusters in a single `execute` call:

```js expandable theme={null}
async () => {
  // Get all clusters
  const clusters = await cnap.request({
    method: "GET", path: "/v1/clusters"
  }).then(r => r.body.data);

  const kube = (clusterId, path) => cnap.request({
    method: "GET",
    path: `/v1/clusters/${clusterId}/kube/${path}`,
  }).then(r => r.body).catch(() => null);

  // Parallel fetch across all clusters
  const results = await Promise.all(clusters.map(async (cluster) => {
    const [nodes, version] = await Promise.all([
      kube(cluster.id, "api/v1/nodes"),
      kube(cluster.id, "version"),
    ]);

    if (!nodes) return { cluster: cluster.name, status: "unreachable" };

    return {
      cluster: cluster.name,
      k8s_version: version?.gitVersion,
      nodes: nodes.items.map(n => ({
        name: n.metadata.name,
        kubelet: n.status.nodeInfo.kubeletVersion,
        os: n.status.nodeInfo.osImage,
        runtime: n.status.nodeInfo.containerRuntimeVersion,
        kernel: n.status.nodeInfo.kernelVersion,
      })),
    };
  }));

  // Find drift
  const versions = [...new Set(results.filter(r => r.k8s_version).map(r => r.k8s_version))];
  return {
    cluster_count: clusters.length,
    unique_k8s_versions: versions,
    drift_detected: versions.length > 1,
    clusters: results,
  };
}
```

## Image Consistency

Compare which container images are running across clusters — catch cases where one cluster is on `v1.2.3` and another is still on `v1.1.0`:

```js expandable theme={null}
async () => {
  const clusters = await cnap.request({
    method: "GET", path: "/v1/clusters"
  }).then(r => r.body.data);

  const kube = (clusterId, path) => cnap.request({
    method: "GET",
    path: `/v1/clusters/${clusterId}/kube/${path}`,
  }).then(r => r.body).catch(() => null);

  const results = await Promise.all(clusters.map(async (cluster) => {
    const pods = await kube(cluster.id, "api/v1/pods");
    if (!pods) return { cluster: cluster.name, images: [] };

    // Deduplicate images
    const images = [...new Set(
      pods.items.flatMap(p =>
        p.spec.containers.map(c => c.image)
      )
    )].sort();

    return { cluster: cluster.name, images };
  }));

  // Find images that differ across clusters
  const allImages = new Map();
  for (const r of results) {
    for (const img of r.images) {
      const name = img.split(":")[0];
      if (!allImages.has(name)) allImages.set(name, new Map());
      allImages.get(name).set(r.cluster, img);
    }
  }

  const drift = [];
  for (const [name, clusterVersions] of allImages) {
    const tags = [...new Set(clusterVersions.values())];
    if (tags.length > 1) {
      drift.push({ image: name, versions: Object.fromEntries(clusterVersions) });
    }
  }

  return { drift_count: drift.length, image_drift: drift };
}
```

## CRD Comparison

Check which Custom Resource Definitions are installed in each cluster — useful for ensuring all clusters have the same operators and extensions:

```js theme={null}
async () => {
  const clusters = await cnap.request({
    method: "GET", path: "/v1/clusters"
  }).then(r => r.body.data);

  const kube = (clusterId, path) => cnap.request({
    method: "GET",
    path: `/v1/clusters/${clusterId}/kube/${path}`,
  }).then(r => r.body).catch(() => null);

  const results = await Promise.all(clusters.map(async (cluster) => {
    const crds = await kube(cluster.id, "apis/apiextensions.k8s.io/v1/customresourcedefinitions");
    return {
      cluster: cluster.name,
      crds: crds?.items.map(c => c.metadata.name).sort() || [],
    };
  }));

  // Find CRDs unique to specific clusters
  const allCrds = new Set(results.flatMap(r => r.crds));
  const missing = [];
  for (const crd of allCrds) {
    const present = results.filter(r => r.crds.includes(crd)).map(r => r.cluster);
    if (present.length < results.length) {
      const absent = results.filter(r => !r.crds.includes(crd)).map(r => r.cluster);
      missing.push({ crd, present_in: present, missing_from: absent });
    }
  }

  return { total_unique_crds: allCrds.size, inconsistencies: missing };
}
```

## Why This Matters

Multi-cluster management typically requires dedicated tools — fleet managers, policy engines, GitOps controllers. For ad-hoc checks and audits, Code Mode gives agents the same cross-cluster visibility without any additional infrastructure.

The agent uses the CNAP API to list clusters, then fans out through each cluster's kube proxy in parallel. It composes the comparison logic — version matching, image diffing, CRD set intersection — inside the sandbox. One conversation, full fleet visibility.
