You
- Run the report generator.
- Read the YAML file it writes.
- Send it to us by email.
git clone https://github.com/Vexa-ai/vexa-delivery
cd vexa-delivery
python3 kit/report/vexa_state_report.py --namespace vexa --dry-run # the commands it will run
python3 kit/report/vexa_state_report.py --namespace vexa # writes state-report.yaml
state-report.yaml — complete, 270 lines
state-report.yaml — complete, 270 lines
# vexa-state-report 0.6.0 — the shape of this deployment's environment.
#
# WHAT THIS IS. The output of one read-only `kubectl get` sweep of a single
# namespace. No database connection, no pods/exec, no credentials of any kind,
# no SQL. Run `--dry-run` to see every command that produced it.
#
# WHY IT EXISTS. You already run Vexa. This says what shape your environment
# is, so what gets built for you works with what you already have and asks for
# nothing you do not.
#
# WHAT IS NOT IN IT. No schema, no rows, no counts, no meeting content, no
# transcripts. No credentials, and no Secret or ConfigMap values — where a
# setting comes from one, only its name is recorded. No node names, no service
# addresses, no ingress hostnames.
#
# IT HAS NOT BEEN SENT ANYWHERE. The tool that wrote it has no transmit path:
# no --submit, no destination, no endpoint. Read it, then send it by hand — or
# do not.
tool: vexa-state-report
tool_version: '0.6.0'
schema_version: 6
generated_at: '2026-08-27T22:47:21Z'
namespace: vexa
read_with: kubectl get -o json, read-only. Nothing else was contacted.
kit: af1725a-dirty
# 1 · PLATFORM — what this runs on. Distribution and version, the cloud
# underneath (read from the node providerID scheme; the instance ids are
# dropped), the node shapes available to schedule on, and the storage classes
# and volumes this cluster already has. Node NAMES are not collected: shapes
# are grouped, which is the same fact without the inventory.
platform:
kubernetes: v1.35.7
distribution: Kubernetes
distribution_note: clusterversion unreadable — plain Kubernetes and a missing ClusterRole look the same
cloud: Linode
node_shapes_note: a taint needs a matching toleration in the values we hand back, or the pod stays Pending
node_shapes:
- count: 1
instance_type: g6-standard-2
region: us-sea
capacity: cpu 2 (1930m allocatable) · memory 3.8Gi (2.7Gi allocatable) · pods 110
taints: none
gpu: null
arch: amd64
os: Debian GNU/Linux 13 (trixie)
kubelet: v1.35.6
runtime: 'containerd://2.2.6'
storage_classes:
- linode-block-storage · linodebs.csi.linode.com · Delete
- linode-block-storage-retain · linodebs.csi.linode.com · Retain · DEFAULT
volumes:
- data-vexa-vexa-minio-0 · 10Gi · linode-block-storage-retain · Bound
- data-vexa-vexa-postgres-0 · 10Gi · linode-block-storage-retain · Bound
- vexa-vexa-agent-workspaces · 10Gi · linode-block-storage-retain · Bound
- vexa-vexa-redis-data · 10Gi · linode-block-storage-retain · Bound
# 2 · VERSIONS AND VALUES — every workload, the image and the digest ACTUALLY
# running, replicas, the requests and limits each container declares, and the
# settings it carries. The digest is what says 0.10 exactly rather than
# roughly. Settings are allowlist-first: a name matching
# password|token|secret|key|apikey is dropped, and so is anything not on the
# allowlist, so what is here is the customisation and nothing else.
# `provided_externally` names the settings that come from a Secret or
# ConfigMap — the name only, never the value. `placement` is where a workload
# is PINNED: the taints above say which nodes repel it, this says where it is
# held. Shape only, and no line means it pins nothing.
workloads:
- kind: Deployment
name: vexa-vexa-redis
chart: vexa-0.12.35
replicas: '1 ready of 1 desired'
placement: affinity podAntiAffinity
containers:
- name: redis
image: >-
valkey/valkey:8-alpine@sha256:e0eb7c480958d32bdc4357a74bdd70653ae15f2f9b4c93c4a5a9fad1dc471c84
registry: docker.io
tag: '8-alpine'
running_digest: 'sha256:e0eb7c480958d32bdc4357a74bdd70653ae15f2f9b4c93c4a5a9fad1dc471c84'
requests: cpu 100m · memory 256Mi
limits: cpu 1 · memory 1Gi
- kind: StatefulSet
name: vexa-vexa-minio
chart: vexa-0.12.35
replicas: '1 ready of 1 desired'
placement: affinity podAntiAffinity
containers:
- name: minio
image: >-
minio/minio:latest@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
registry: docker.io
tag: latest
running_digest: 'sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e'
requests: cpu 100m · memory 256Mi
limits: cpu 500m · memory 1Gi
- kind: StatefulSet
name: vexa-vexa-postgres
chart: vexa-0.12.35
replicas: '1 ready of 1 desired'
placement: affinity podAntiAffinity
containers:
- name: postgres
image: >-
postgres:17-alpine@sha256:18cfe3ef5e6815560c98237d6216d1e5119702fb0f3894c8785dd58b8bbe5d73
registry: docker.io
tag: '17-alpine'
running_digest: 'sha256:18cfe3ef5e6815560c98237d6216d1e5119702fb0f3894c8785dd58b8bbe5d73'
requests: cpu 200m · memory 512Mi
limits: cpu 4 · memory 4Gi
# 3 · RESOURCES — the namespace's quotas and LimitRanges, and the containers
# that declare no resources of their own. This is here because it broke a real
# upgrade: a ResourceQuota covering cpu or memory makes Kubernetes refuse any
# pod that declares neither request nor limit, unless a LimitRange supplies a
# default. Vexa's per-meeting bot pods declare none by design. `finding` below
# is present only when all three conditions hold at once.
resources:
quotas: []
limit_ranges: []
containers_declaring_no_resources: []
# 4 · WIRING — which components exist and how they are connected. The database
# and transcription especially: in-cluster or external, how each is ADDRESSED
# (the setting's name, never its value), version, and GPU or CPU. Every fact
# here was read from the cluster. Nothing connected to the database. Exposure
# is read as Ingress AND as OpenShift `Route`, because an OpenShift estate has
# no Ingress and reading one alone would say positively that nothing is
# exposed.
wiring:
database:
where: in-cluster
workload: vexa-vexa-postgres
version: '17-alpine'
image: >-
postgres:17-alpine@sha256:18cfe3ef5e6815560c98237d6216d1e5119702fb0f3894c8785dd58b8bbe5d73
resources: cpu 200m · memory 512Mi
addressed_by: null
redis:
where: in-cluster
workload: vexa-vexa-redis
version: '8-alpine'
image: >-
valkey/valkey:8-alpine@sha256:e0eb7c480958d32bdc4357a74bdd70653ae15f2f9b4c93c4a5a9fad1dc471c84
resources: cpu 100m · memory 256Mi
addressed_by: null
transcription: []
exposure_note: 'type, class, ports and TLS below; no address, host or DNS name'
services:
- vexa-vexa-admin-api · ClusterIP · 8001/TCP
- vexa-vexa-agent-api · ClusterIP · 8100/TCP
- vexa-vexa-gateway · ClusterIP · 8000/TCP
- vexa-vexa-meeting-api · ClusterIP · 8080/TCP
- vexa-vexa-minio · ClusterIP · 9000/TCP,9001/TCP
- vexa-vexa-postgres · ClusterIP · 5432/TCP
- vexa-vexa-redis · ClusterIP · 6379/TCP
- vexa-vexa-runtime · ClusterIP · 8090/TCP
- vexa-vexa-terminal · ClusterIP · 3000/TCP
ingresses: []
routes: 'absent — an OpenShift kind; not-present and not-granted are the same read here'
# 5 · REGISTRY — where images actually come from, so an upgrade does not sit
# in ImagePullBackOff behind a corporate mirror. Observed from image
# references, pull credentials on the pod specs AND on the ServiceAccounts,
# and any readable mirror configuration — never inferred. Whether a pull
# SUCCEEDS is unreadable and is asked as a QUESTION at the end.
registry:
registries_referenced:
- docker.io (3 containers)
image_pull_credentials:
- vexa-channel-registry (on serviceaccount default)
image_pull_credentials_note: 'the NAMES of the imagePullSecrets referenced; nothing in them is read'
cluster_mirror_config: null
cluster_mirror_note: >-
ImageDigestMirrorSet is OpenShift and cluster-scoped, and was not readable
(kubectl get imagedigestmirrorsets.config.openshift.io exited 1) — what a
non-OpenShift cluster and a missing ClusterRole both look like. A
runtime-level mirror is invisible to every kubectl read anyway.
reachability: >-
every image reference points at a public registry (docker.io), with 1 pull
secret(s) attached. Whether the pull goes direct or through a transparent
proxy is not observable from here.
# 6 · ADMISSION — what this namespace will LET run: its Pod Security labels
# and, on OpenShift, its SCC ranges. This decides whether what we deliver runs
# non-root with a seccomp profile and all capabilities dropped — and, under
# SCC restricted-v2, that it pins no UID of its own, since one outside the
# range below is rejected rather than mutated. Unreadable is absent, never
# permissive.
admission:
pod_security: >-
no pod-security.kubernetes.io labels on this namespace — whatever the
cluster's own default is applies, and this read cannot see what that is
openshift_scc: 'no openshift.io/sa.scc.* annotations — what plain Kubernetes looks like'
# 7 · NETWORK POLICY — whether anything here can reach a registry. A policy
# that default-denies egress is why an install sits unable to resolve DNS or
# pull an image, and it appears nowhere else in this document. Shape only:
# rule bodies carry internal addresses and building a bundle does not need
# them.
network_policy:
policies: []
egress: no NetworkPolicies in this namespace — nothing here isolates it
# 8 · THE INSTALL ALREADY HERE — the release name and what manages it, read
# from the labels the workloads carry: no helm binary was run, no Secret was
# read. An upgrade under a release name that matches nothing does not fail —
# it installs a SECOND copy of the estate beside the running one, against the
# same database, and reports success. Argo CD or Kyverno already here makes
# this an adoption; `openshift-gitops` is checked too, because on OpenShift
# the supported Argo is the operator's and a second beside it is that same
# footgun. `cluster_scope` is derived from what this run was ALLOWED to read.
install:
release_names:
- vexa (3 workloads)
managed_by: Helm
cluster_scope: nodes and storage classes readable — admission can ship cluster-wide
argo_cd: present in namespace argocd · 6 deployments · version v3.5.1
openshift_gitops: absent — namespace openshift-gitops exists but runs no deployments
kyverno: present in namespace kyverno · 4 deployments · version v1.19.0
# WHAT COULD NOT BE READ, AND WHAT CANNOT BE. Recorded rather than defaulted
# to zero or an empty list: a fabricated zero in a document whose whole
# purpose is to say what you already have is worse than a stated gap. Most
# rows are reads this run was not granted; a row marked QUESTION is one no
# read-only call can answer at all, so the answer is a sentence from you.
absent:
- what: transcription
reason: >-
no workload matched 'whisper|transcri|asr|diariz|stt' — widen it with
--transcription-match. ABSENT, not 'CPU'.
- what: routes
reason: kubectl get routes.route.openshift.io exited 1 — how an OpenShift estate is exposed
- what: 'QUESTION: can this cluster pull a release image by digest'
reason: proving it needs a probe pod, which is a write
- what: 'QUESTION: does the registry need a CA bundle, or plain HTTP'
reason: TLS trust is in no object readable from here
# WHAT THE TOOL REFUSES, each enforced by code rather than intent. Read the
# source: it is one file, Apache-2.0, and `--dry-run` prints every command.
refuses:
- 'transmit: there is no submit path in this tool'
- 'write: every call is kubectl get or kubectl version; no other verb exists here'
- 'connect: no database client, no SQL, no flag that would take a password'
- 'content: no schema, no rows, no counts, no meeting data, no Secret values'
# THE REDACTION SELF-CHECK. `withheld_values` counts the values this run SAW
# and chose not to record. The finished document is then scanned for every one
# of them; `verified: true` means none survived. A survivor exits 3 and names
# the count, never the value.
redaction:
rule: >-
values under keys matching password|token|secret|key|apikey become
REDACTED; settings capture is allowlist-first, so most values are never
written rather than redacted after
withheld_values: 3
verified: true
leaks: 0
Us
- We build your bundle and give you a channel — its name, its public key, and a pull-only credential.
- You install Vexa Delivery in your cluster: pinned Argo CD, pinned Kyverno, the admission policy, after a preflight that fails closed.
- You subscribe, and your staging syncs. Promotion to production is yours to make.
- It stays current from there, as we publish — how that works, and what’s proven where.