Skip to content

lookout scan

Start here when you know something is wrong but not what: one call runs every target-free incident check across the cluster — broken workloads, dead admission webhooks, stuck volumes and PVCs, rejected Gateway routes, config drift — then drills into the dependency edges of whatever it flagged. Needs no target; --include=audit adds the posture sweep.

MCP tool: k8s_scan (MCP profile: triage, audit)

Terminal window
lookout scan [flags]
FlagTypeDefaultMeaning
--includestring—additionally run these opt-in groups: audit,cloud,perf. Comma-separated, ‘all’ for every one, ’-’ to subtract (all,-cloud). Left out by default because they answer a different question (audit = posture, no incident) or need a provider build (cloud, perf)
--max-drilldownint20cap the stage-2 dependency-edge drill-down at this many workloads, worst severity first (0 disables it); the number dropped is reported as truncated= in the summary
--cert-warnduration720hreport TLS certificates expiring within this window (drill-down stage; same meaning as state edges --cert-warn)
FlagTypeDefaultMeaning
--namespacestring—limit the scan to one namespace
-Abool—scan all namespaces
--workloadstring—target one workload as <Kind>/<namespace>/<name>, e.g. Deployment/prod/api
--sinceduration—how far back to look (0 = command default)
--formatstringlogfmtoutput format: logfmt|json (one record per line either way)
--timeoutduration1m0sabort the invocation after this long (exit 1)
--kubeconfigstring—path to a kubeconfig file, instead of $KUBECONFIG / ~/.kube/config
--contextstring—kubeconfig context to read, instead of its current-context. Selects a cluster for THIS invocation only — nothing is written back — so concurrent invocations can target different clusters. Reported as context=<name> in the summary line
--exemptionsstring—path to a git-reviewed exemption file (YAML); covered findings are ANNOTATED with their reason and expiry and counted as exempt=<n> in the summary, never dropped

Every kind= this command can emit, and the severities it carries them at. Nothing else appears in its output; a kind absent from a run means the check looked and found nothing. See the finding-kind glossary for the whole vocabulary.

KindSeverityClaim
scan.check_skippedinfoa stage declined this invocation because a zero-argument scan cannot supply something it needs — the coverage claim is smaller than it looks
scan.check_failedwarninga stage errored; the scan continued without it, so this run saw less than a whole cluster — unless EVERY stage failed and none read anything, which is a runtime error (exit 1) rather than a scan
scan.incompletewarningthe —timeout expired with stages still to run; not_run names them
pod.crashloopcriticala container is crash looping
pod.imagepullcriticala container cannot pull its image
pod.waitingwarninga container is stuck in an error waiting state (CreateContainerConfigError, InvalidImageName, …)
pod.oomkilledwarninga container’s last termination was an OOM kill
pod.restartswarninga container has restarted at least —restarts times
pod.notreadywarninga container in a Running pod has been not-ready past the —pending-age grace
pod.failedwarningthe pod reached phase Failed
pod.pendingcritical, warningthe pod has been Pending longer than —pending-age with no container-level diagnosis; critical when the scheduler has declared it Unschedulable, which is a capacity or constraint problem rather than latency
workload.replicafailurecriticalthe controller cannot create pods at all (quota, PodSecurity, admission) — no pod exists to diagnose
workload.stalledcriticala Deployment’s Progressing condition is False: the rollout has given up
workload.rolloutcritical, warningreplicas are short of desired; critical when nothing is serving at all
job.failedwarninga Job’s Failed condition is set
cron.missedcritical, warningan unsuspended CronJob’s schedule said to run more than —cron-grace ago and status says it did not; critical once several activations in a row are gone
cron.unparseablewarninga CronJob’s spec.schedule could not be parsed, so its activations cannot be judged at all
node.notreadycriticalthe node’s Ready condition is not True
node.pressurecriticalthe node reports Memory/Disk/PID pressure
node.conditioncritical, warninga non-standard node condition is True — NPD and its cousins publish problems that way
node.cordonedwarningthe node is unschedulable but still holds pods: a stuck drain or a forgotten maintenance step
node.preemptcritical, warning, infoa reclaim taint marks the node for termination; severity tracks how imminent
pdb.gridlockedcritical, warningthe budget permits no disruptions; critical when healthy pods are already below the required minimum
addon.degradedcritical, warninga kube-system add-on (dns, proxy, cni, csi, metrics, connectivity) is short of replicas; critical when none are available
quota.nearwarninga ResourceQuota resource is at or past —quota-warn percent of its hard limit
quota.exhaustedcriticala ResourceQuota resource is at its hard limit: the next create is rejected
webhook.failing_closedcriticalthe webhook has no working backend and failurePolicy=Fail: every gated write is rejected cluster-wide
webhook.dead_backendwarningthe webhook’s service backend is missing, has no ready endpoints, or does not serve the named port
webhook.slow_riskinfothe webhook’s timeout is long enough to slow every gated write if the backend degrades
webhook.ca_expiredcriticalthe webhook’s caBundle has expired: the API server cannot verify it
webhook.ca_expiringwarningthe webhook’s caBundle expires within —cert-warn
volume.multi_attachcriticalan RWO claim is wanted by pods on more than one node — the second pod never starts
volume.zone_conflictcriticalthe PV is locked to a zone the pod’s node is not in
volume.attach_errorcritical, warningthe attach or detach is failing; critical once it has been failing long enough to be stuck rather than slow
volume.orphaned_attachmentinfoa VolumeAttachment survives its PV or its node
storage.missing_classcriticalthe claim names a StorageClass that does not exist — it will stay Pending forever
storage.no_default_classcriticalthe claim names no class and the cluster has no default StorageClass
storage.no_provisionerwarningthe claim’s class is static-only (kubernetes.io/no-provisioner) and no matching PV is available
storage.multiple_defaultswarningmore than one StorageClass is annotated as the cluster default; which one wins is not defined
storage.pv_failedwarninga PersistentVolume is Failed: its reclaim did not complete, so the backing disk stays allocated and the volume cannot be reused
storage.pv_releasedinfoa PersistentVolume is Released — retained on purpose, but its capacity is unusable until spec.claimRef is cleared
gateway.missing_classcriticalthe Gateway names a GatewayClass that does not exist — nothing will program it
gateway.class_not_acceptedcriticalthe Gateway’s GatewayClass is not Accepted by its controller
gateway.not_acceptedcriticalthe Gateway itself is not Accepted
gateway.not_programmedcriticalthe Gateway is Accepted but not Programmed: no data plane is carrying its traffic
gateway.listener_invalidwarningone listener of an otherwise working Gateway is not resolved or not programmed
route.missing_parentcriticalthe route’s parentRef names a Gateway that does not exist
route.not_acceptedcriticalthe Gateway refused the route’s attachment (listener, hostname, or namespace policy)
route.missing_backendcriticalthe route’s backendRef Service does not exist
route.backend_portcriticalthe route’s backendRef Service exists but does not expose the named port
crd.unavailableinfothe API group this check reads is not served by the cluster, so nothing was examined (no coverage lies)
wi.gsa_missingcriticalthe annotated Google service account does not exist — every GCP call from these pods fails
wi.unboundcriticalthe KSA annotates a GSA but the roles/iam.workloadIdentityUser binding is missing or malformed
wi.unannotated_useinfoa pod sets GOOGLE_APPLICATION_CREDENTIALS but its ServiceAccount carries no Workload Identity annotation
cloud.unavailableinfothe cloud capability this check needs is unavailable, so nothing was examined — an explicit degradation record, never silence
drift.manual_editcritical, warninga manager other than the GitOps controller owns spec fields on this object; critical when one of them is high blast radius (image, replicas, env)
audit.workload_identity_offwarningWorkload Identity is off cluster-wide, or a node pool bypasses it — pods authenticate to the cloud as the node
audit.legacy_metadatawarninga node pool still serves the pre-v1 instance-metadata endpoints, which any pod can read
audit.public_control_planewarning, infothe control-plane endpoint is reachable from the internet; info when authorized networks narrow it
audit.exemption_expiredwarningan exemption entry has lapsed: the findings it used to annotate are being reported unqualified again
audit.exemption_expiringinfoan exemption entry lapses within —within — renew it or let it go deliberately
audit.privileged_containerwarninga container runs privileged or holds a node-root capability (ALL, SYS_ADMIN): a container escape is a node compromise
audit.host_namespacewarningthe pod shares the node’s network, PID, or IPC namespace
audit.hostpath_mountwarning, infothe pod mounts a host path; warning when it is writable, info when read-only
audit.default_sa_automountwarningthe pod runs as the namespace’s default ServiceAccount with its token automounted, and something in the pod can use it
audit.podsecurity_gapswarningthe namespace enforces no Pod Security Admission level, so none of the above is prevented
audit.netpol_missingwarning, infonothing restricts this direction for the subject — a namespace with no policy at all, or a workload the covering policies’ selectors miss; info for the egress direction, where no policy is a defensible default
audit.version_behindwarning, infothe control plane or a node pool is behind what the provider publishes, or a node pool has skewed from the control plane; info while the gap is still within the supported skew
audit.upgrade_unmanagedwarningnothing will close that gap on its own: no release channel, or node auto-upgrade/auto-repair off
audit.upgrade_blockedwarning, infoan active maintenance exclusion, or a node image on the removed Docker runtime, will stop the upgrade when it comes
audit.upgrade_unattendedinfoupgrades will happen with nobody watching: no maintenance window, or no upgrade notifications
audit.no_pdbwarningthe workload has no PodDisruptionBudget: a drain can take every replica at once
audit.single_replicawarningthe workload runs a single replica, so any disruption is an outage
audit.no_readiness_probewarninga container has no readiness probe, so traffic reaches it before it can serve
audit.no_liveness_probeinfoa container has no liveness probe, so a wedged process is never restarted
audit.no_spreadinfothe workload’s replicas are not spread across nodes or zones
audit.rigid_schedulingwarning, infoplacement constraints pin the workload to too few nodes to survive losing one
audit.hpa_cannot_scalewarningthe autoscaler structurally cannot scale: min equals max, the target is missing, or a container has no request for its utilization target to divide by
audit.suspended_cronjobwarninga CronJob has been suspended past —cron-suspended and has skipped activations because of it: whatever it does is not happening, and nothing else reports that
ipspace.rangecritical, warning, infoa pod/service/node range is at 80% of its CIDR or worse; critical from 95%, info for a range the cloud APIs cannot rate and for an —all row below the line
orphan.diskwarninga GCE disk has been unattached for at least —min-age and is still billing
orphan.lbwarninga forwarding rule or load balancer routes to zero endpoints and is still billing
quota.pressurecritical, warning, infoa cloud quota is at or above —quota-warn percent of its limit; critical from 95%, info for an —all row below the line
stockout.zonewarningthe cloud had no capacity for a machine type in this zone during the window — the reason a scale-up failed and pods stayed Pending
perf.apiserver_p99critical, warningapiserver request latency p99 crossed the pack threshold for a verb/resource — warning from 1s, critical from 4s
perf.apf_saturationcritical, warningan API Priority and Fairness level is holding a sustained queue — warning from 10 queued, critical from 100
perf.apf_rejectscritical, warningAPF is shedding load: the apiserver is returning 429s at a priority level
perf.etcd_fsynccritical, warningetcd WAL fsync p99 crossed the pack threshold — warning from 10ms, critical from 100ms
perf.etcd_db_sizecritical, warningthe etcd database is approaching its quota — warning from 4 GiB, critical from 5.5 GiB
perf.startup_p95critical, warningpod first-ready p95 crossed the pack threshold — warning from 60s, critical from 300s
perf.pack_unavailablewarninga metric the requested pack needs is not in the metrics workspace, so part of the pack could not run; the rest still did (no coverage lies)
edge.missing_refcriticala referenced ConfigMap, Secret, ServiceAccount, TLS secret, IngressClass, StorageClass, or governing Service does not exist
edge.missing_keycriticalthe referenced key is absent from an existing ConfigMap/Secret
edge.invalid_refwarningthe referenced object exists but is the wrong type to serve the reference
edge.unclassedwarningthe Ingress names no class and no IngressClass declares itself the cluster default — no controller will claim it
edge.selector_emptycriticala Service selector selects zero pods, so the service routes nowhere
edge.selector_unreadycritical, warningthe Service selects pods but some are not Ready; critical when none are
edge.endpoints_missingcriticala selecting Service has no EndpointSlices at all
edge.endpoints_orphanedwarningan endpoint targetRef names a pod that no longer exists
edge.endpoints_unreadycritical, warningthe endpoint ready-count disagrees with the selected pods (stale or lagging slices); critical at zero ready
edge.backend_missingcriticalan Ingress backend service, or the port it names, does not exist
edge.cert_expiredcriticala TLS certificate’s NotAfter is in the past
edge.cert_expiringwarninga TLS certificate expires within —cert-warn
edge.cert_invalidwarningtls.crt is missing or unparseable, or the secret is not kubernetes.io/tls
edge.rbac_danglingwarninga (Cluster)RoleBinding for the workload’s ServiceAccount points at a missing (Cluster)Role

Beyond the shared envelope fields (kind, severity, namespace, kind_of_object, name, reason, message, fingerprint, exempt_reason, exempt_expires):

FieldMeaning
checkwhich registered command produced this finding — also the command to run for the full detail behind it
not_runon scan.incomplete: the checks the timeout left unrun, comma-separated
checkssummary-line note: how many checks this scan actually ran
skippedsummary-line note: opt-in groups this scan did NOT run (switch one on with —include=<group>) — stated so a quiet scan is never mistaken for a complete one
drilldownsummary-line note: workloads the stage-2 dependency-edge drill-down covered
truncatedsummary-line note: drill-down candidates dropped by —max-drilldown
containercontainer the finding is about (init containers prefixed init:)
imageimage reference that failed to pull
restartscontainer restart count
exit_codeexit code of the container’s last termination
last_statereason of the container’s last termination (e.g. OOMKilled)
agehow long the abnormal state has persisted
desireddesired replica/scheduled count from spec
readyready count from status
updatedupdated-to-current-revision count from status
availableavailable count from status
failedfailed pod count of a Job
schedulea CronJob’s spec.schedule
expectedthe activation a CronJob should have run and did not
missed_runsactivations missed since the anchor; ≥N when the walk was capped
anchorwhat the missed count was measured from: last_schedule or creation
time_zonea CronJob’s spec.timeZone, when set
last_schedulea CronJob’s status.lastScheduleTime, or never
active_jobsJobs a CronJob still has running
conditionnode condition type that is abnormal
tainttaint key indicating reclaim/drain
podspods affected (behind a cordoned node or a PDB)
healthycurrently healthy pods behind a PDB
requiredpods the PDB requires healthy
addonsystem add-on role: dns, proxy, cni, csi, metrics, connectivity
resourceResourceQuota resource name at or near its limit
usedquota usage from status
hardquota hard limit from status
pctquota usage as percent of the hard limit
webhookadmission webhook as <configuration>/<webhook name>
serviceservice backend the webhook points at, as <namespace>/<name>
backendwhy the backend is dead: service missing, no ready endpoints, or port <p> not on service
gatesnamespaces the webhook gates, from namespaceSelector: all namespaces, or <matched>/<total> namespaces with up to 5 names
rulescompact operations/resources summary of the webhook’s rules, e.g. “CREATE,UPDATE pods,deployments.apps”
object_selectorthe webhook’s objectSelector, when one is set
timeoutwebhook timeoutSeconds as <n>s (nil defaults to the API’s 10s)
subjectCA-bundle certificate subject (CN when set); never key material
not_afterCA-bundle certificate NotAfter, RFC 3339
days_leftwhole days until NotAfter (negative = expired)
nodesdistinct nodes those pods are scheduled on, sorted
access_modesthe claim’s declared access modes
pvPersistentVolume behind the claim or attachment
pvcPersistentVolumeClaim the pod mounts (same namespace as the pod)
nodenode the attachment targets or the pod is scheduled on
attacherCSI driver responsible for the attachment (spec.attacher)
errorthe attach/detach error message, truncated to 200 chars
attachedthe attachment’s status.attached at scan time
pv_zoneszones the PV’s node affinity allows, sorted
node_zonezone label of the node the pod is scheduled on
orphanwhich referenced side is gone: “pv missing”, “node missing”, or both
storage_classStorageClass the claim names, or the class the finding is about
classesStorageClasses the cluster does have, sorted (empty when there are none)
defaultsStorageClasses annotated as the cluster default, sorted
provisionerthe class’s spec.provisioner
phasethe claim’s or volume’s status.phase at scan time
requestedstorage the claim requests (spec.resources.requests.storage)
capacitythe volume’s spec.capacity.storage
reclaim_policythe volume’s spec.persistentVolumeReclaimPolicy
claimthe claim the volume was bound to, as namespace/name
binding_modethe class’s volumeBindingMode (Immediate when unset)
gateway_classGatewayClass the Gateway names
controllerthe GatewayClass’s spec.controllerName — which implementation owns it
gatewayGateway the route attaches to, as namespace/name
listenerlistener name within the Gateway
portlistener port, or the backendRef port the Service does not expose
protocollistener protocol
service_portsports the backend Service does expose, sorted
api_groupcrd.unavailable: the API group-version this command needed
resourcescrd.unavailable: the resources it would have read
unavailablesummary-line note: why the group could not be read (absent CRDs, or discovery denied)
gsathe cloud identity (GSA email) the ServiceAccount’s annotation claims
problemmachine-matchable problem code from the provider (e.g. no-workload-identity-binding)
envthe credential-file env var found (GOOGLE_APPLICATION_CREDENTIALS)
capabilitycloud.unavailable: the provider capability this command needed (workload-identity)
providercloud.unavailable: the provider that was asked
manageron findings: the foreign manager string from managedFields (a tool name like kubectl-edit — never a user identity; see —identity); on the summary line: the resolved GitOps manager
detectionsummary note: how the GitOps manager was resolved — declared (—manager), majority (auto-detected recognized GitOps controller owning >50% of the spec leaf fields in scope), or none (no manager resolved; nothing emitted)
detection_reasonsummary note on detection=none, naming why: no-spec-fields-in-scope (nothing in scope owns a spec field), no-majority-manager (a leading candidate exists but owns 50% or less), or not-a-gitops-manager (the majority owner is not a recognized GitOps controller — e.g. kubeadm or a kubectl manager on a cluster with no GitOps at all)
candidatesummary note on detection=none: the leading manager that fell short (of the majority, or of being a recognized GitOps controller) — pass it to —manager if it is in fact the GitOps controller
sharesummary note: the resolved manager’s (or, on detection=none, the candidate’s) percentage of every spec leaf field owned across the scanned objects, rounded. A declared manager with a low share means most findings are other legitimate owners
unmanagedsummary note, omitted at zero: scanned objects the resolved GitOps manager owns no spec field on. Nothing is reported for them — an object the manager never applied cannot have drifted from it — so a high count next to zero findings means the manager’s scope is narrower than the scan’s
operationmanagedFields operation of the foreign manager’s last write: Apply or Update
toolclient tool recognized from the manager string (kubectl for kubectl-edit/kubectl-patch/kubectl-*)
fieldscompact spec paths the foreign manager owns (e.g. spec.template.spec.containers[app].image), capped at 8 with a +N more tail
field_counttotal spec leaf fields the foreign manager owns on this object (uncapped)
principal—identity: the audited principal of the write nearest the drift time (GKE: principalEmail), or the explicit sentinel none-in-audit-window / no-write-time-anchor when the trail cannot answer
principal_agent—identity: the caller-supplied client string of that write (a kubectl or controller user-agent), when the trail records one; caller-controlled text, display-only
other_principals—identity: other distinct principals that wrote the object inside the audit window, capped at 8 with a +N more tail
identitysummary note when —identity could not be served: the unavailable marker naming why (no provider / audit capability absent)
clusteron a node-pool finding: the cluster the pool belongs to, so the record stands alone
workload_poolthe cluster-wide workload identity pool that this node pool’s pods bypass
metadata_modehow the node pool exposes instance metadata to pods: node-identity means any pod can mint tokens for the node’s service account
disable_legacy_endpointsthe pool’s legacy-metadata setting as the provider records it: enabled when someone turned the pre-v1 endpoints back on, unset when the pool was never configured either way
node_poolssummary note: node pools examined — the cluster itself is the other unit scanned counts
endpointthe control plane’s internet-facing address
authorized_networkshow many source ranges the allow-list permits
authorized_network_cidrsthose ranges, sorted as the provider returned them and capped at 8 with a +N more tail
gcp_public_cidrswhether the provider’s own public ranges are admitted in addition to the allow-list
exempt_kindthe finding kind the entry covers — this is the entry’s kind: field, not this finding’s own kind
expireswhen the entry stops applying, RFC 3339 (a bare YYYY-MM-DD in the file resolves to 00:00:00Z that day)
expired_forhow long ago the entry lapsed, rounded to whole days — only on audit.exemption_expired
expires_inhow long until the entry lapses, rounded to whole days — only on audit.exemption_expiring
ownerthe entry’s owner: field, absent if it has none — which is itself worth fixing, since “expired, and nobody knows whose it was” is where these files end up
justificationthe entry’s reason: field: why the exempted finding was accepted. Distinct from the envelope’s exempt_reason, which is the justification for THIS finding being exempt
containerscontainers implicated by the finding — those running privileged, or holding a node-root capability
container_namestheir names, capped at 8 with a +N more tail
total_containerscontainers in the pod template, init containers included, so containers reads as a fraction
capabilitiesthe node-root capabilities added by those containers (ALL, SYS_ADMIN), sorted and deduplicated
host_pathshostPath volumes the template mounts; a declared but unmounted hostPath volume grants no access and is not counted
host_path_namesthe paths on the node, sorted and capped at 8
service_accountthe ServiceAccount the finding is about — always default, the one every pod gets when its template names none
mounting_workloadsworkloads in the namespace running as the default ServiceAccount without disabling automount at the pod level; the finding does not fire at 0
mounting_workload_namestheir Kind/name, sorted and capped at 8
pss_enforcethe namespace’s pod-security.kubernetes.io/enforce label, omitted when unset
pss_warnits /warn label, omitted when unset — set without /enforce means the namespace is in dry-run
pss_auditits /audit label, omitted when unset — same dry-run meaning
workloadspod templates this pass judged in the namespace, so an unenforced namespace with nothing in it reads differently from a busy one
namespacessummary note: namespaces examined — the denominator for every namespace-subject claim, which scanned (pod templates) does not cover
policiesNetworkPolicies in the namespace naming this direction in policyTypes; 0 on a namespace-subject finding, and the number that failed to select the subject on a workload one
total_policiesNetworkPolicies in the namespace in either direction, so an egress-only namespace does not read as an empty one
host_network_workloadspod templates excluded because they use the node’s network namespace, where NetworkPolicy does not apply; omitted at 0
covered_workloadspod templates in the namespace that ARE selected for this direction — the neighbours the subject fell out of step with
pod_labelsthe template’s own labels, which are what the policies’ selectors failed to match, sorted and capped at 8
versionthe current version of the finding’s subject — the control plane’s, or the node pool’s
target_versionthe version the provider would move this cluster to: its channel’s upgrade target where one is published, otherwise the channel’s default
control_plane_versionon a node-pool skew finding: the control-plane version the pool is measured against
minor_versions_behindhow many minor releases separate the two versions
channelthe release channel the cluster is subscribed to, and the one whose published versions the comparison used; none when it is subscribed to no channel
image_typethe provider’s name for the node image the pool runs
exclusionthe operator’s name for the maintenance exclusion currently in force
scopehow much of the upgrade stream that exclusion holds back: all-upgrades, minor-upgrades or minor-and-node-upgrades
endswhen the exclusion lifts, or end-of-support for one that runs until the cluster’s version leaves support
days_remaininghow much longer the exclusion has left to run
replicasthe workload’s spec.replicas (nil defaults to 1, matching the API server); absent on DaemonSets, whose replica count is the node count
namespace_pdbsPodDisruptionBudgets in the workload’s namespace — 0 says the namespace has no PDB culture at all, a non-zero value says this workload was missed
min_replicasthe HPA’s spec.minReplicas (nil defaults to 1, matching the API server)
max_replicasthe HPA’s spec.maxReplicas
metricthe utilization metric the HPA cannot compute, comma-separated if more than one
scale_targetthe HPA’s scaleTargetRef as Kind/name
eligible_nodesnodes satisfying the workload’s REQUIRED placement constraint; an upper bound, since taints and cordons are not subtracted
cluster_nodesnodes in the cluster, so eligible_nodes reads as a fraction
constraintthe label and field keys that narrow placement, sorted and capped at 8
suspended_forhow long spec.suspend has been true, rounded to whole days
suspended_sincewhen the suspension is estimated to have started, RFC 3339
pdbssummary note: PodDisruptionBudgets seen in scope
hpassummary note: HorizontalPodAutoscalers seen in scope
cidrthe range’s CIDR block
purposewhat the range allocates: pods, services, or nodes
zoneorphan.disk: the disk’s zone
size_gborphan.disk: provisioned size in GB (billed whether used or not)
disk_typeorphan.disk: disk type short name (pd-ssd bills ~4x pd-standard idle)
unused_sinceorphan.disk: last detach (or creation, if never attached), RFC3339; omitted when the provider cannot date it
unused_fororphan.disk: how long the disk has been unattached; “unknown” when undatable
regionorphan.lb: the forwarding rule’s region (“global” for global rules)
whyorphan.lb: the provider’s orphan judgment (e.g. which backend resolved empty)
usagecurrent usage in the quota’s own unit
limitthe quota limit
unitthe quota’s unit, when the provider names one
machine_typethe exhausted machine type (omitted when the log record does not name one)
eventsstockout events for this zone/machine-type pair in the window
first_seenearliest event in the window (RFC3339)
last_seenlatest event in the window (RFC3339)
reroutesame-region zones active in the window with no stockout for this machine type, comma-separated; omitted when the window offers no clean candidate
windowsummary-line note: the lookback the events cover
packthe pack this finding belongs to; also the summary-line note naming the pack that ran
verbapiserver request verb for this series (apiserver pack)
priority_levelAPF priority level for this series (apf pack)
codethe HTTP status code the query matched (apf pack: 429)
observedthe worst (maximum) aligned value in the window, in the query’s unit — the breach basis
latestthe newest aligned value in the window
thresholdthe crossed threshold: the critical one when severity=critical, else the warning one
trendstartup pack: second-half vs first-half mean delta of the window, e.g. “+34%” — the p95 trend direction
workloadthe target the edges were traced from as <Kind>/<namespace>/<name>, stamped on every finding — a workload, or the Service itself when entered from the service side
likely_workloadon a Service-entry edge.selector_empty: the workload in that namespace whose pod labels best fit the broken selector, i.e. the one it was probably meant to select. Absent when two workloads fit equally well, because then naming one would be a guess
volumepod volume, or StatefulSet volumeClaimTemplate, whose reference is broken
keythe referenced key that is missing from the ConfigMap/Secret
selectorthe Service label selector under scrutiny
selectedpods the Service selector currently selects
endpointstotal endpoints across the Service’s EndpointSlices
sliceshow many EndpointSlices back the Service
podpod named by an orphaned endpoint targetRef
viahow the broken reference is reached from the workload: mount, ingress, or imagePullSecret
ingressIngress referencing the TLS secret, or the unserved Ingress itself
hostIngress rule host of the broken backend (empty for the default backend)
pathIngress rule path of the broken backend
role_refdangling roleRef as <Kind>/<name>

Output: one finding per line (logfmt; —format=json for one JSON object per line), keys in fixed order; healthy resources emit nothing. The final line is always the summary: scanned= findings= elapsed= — findings=0 with a summary present means “scanned and healthy”; a stream without a summary line is void. Exit 0 data, 1 runtime error (diagnostics on stderr only), 2 usage.

Terminal window
lookout scan
lookout scan --namespace=prod
lookout scan --include=audit --format=json
lookout scan --max-drilldown=0