Trust center
Read-only by default, with write access you control
The KubeOn agent only reads. KubeOn changes a cluster only through pull requests or a separate controller you turn on, feature by feature, and you can pause it at any time.
Security at a glance
Read-only agent
get, list and watch only. No Secrets, no ConfigMaps. Write access is a separate, opt-in controller.
Outbound only
The hub never calls into your clusters. Agents push snapshots out over HTTPS.
Exports stay in your account
Billing exports are read in place through identities that cannot change them.
Encrypted and isolated
Encrypted at rest with UnitVane-managed keys and in transit with TLS 1.2 or later. Each organization's data is kept apart.
Least-privilege IAM
Billing roles read the export and write only query results. Agent roles can only add snapshots.
SSO and team scope
SAML 2.0 or OIDC sign-in, four roles, and team leads limited to their teams.
Audit log
Every mapping, policy, integration and proposal decision, with who and when.
Scanned supply chain
Images scanned on every build; high and critical findings block a release.
Published permissions
Agent and billing permissions, in the open
These are the exact rules the agent's ClusterRole and the AWS billing role contain. Azure and Google Cloud use the roles listed in the docs: Storage Blob Data Reader and Cost Management Reader on Azure, BigQuery Data Viewer and Job User on Google Cloud.
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: kubeon-agent-readerrules: - apiGroups: [""] resources: [nodes, pods, namespaces, persistentvolumes, persistentvolumeclaims, services] verbs: [get, list, watch] - apiGroups: [apps] resources: [deployments, statefulsets, daemonsets, replicasets] verbs: [get, list, watch] - apiGroups: [batch] resources: [jobs, cronjobs] verbs: [get, list, watch] - apiGroups: [storage.k8s.io] resources: [storageclasses] verbs: [get, list, watch] - apiGroups: [metrics.k8s.io] resources: [nodes, pods] verbs: [get, list] - apiGroups: [karpenter.sh] # NodePools, on Karpenter clusters resources: [nodepools, nodeclaims] verbs: [get, list, watch] - apiGroups: [autoscaling] resources: [horizontalpodautoscalers] verbs: [get, list, watch] - apiGroups: [argoproj.io] # where workloads are deployed from resources: [applications, rollouts] verbs: [get, list, watch] - apiGroups: [kustomize.toolkit.fluxcd.io, helm.toolkit.fluxcd.io, source.toolkit.fluxcd.io] resources: [kustomizations, helmreleases, gitrepositories] verbs: [get, list, watch]{ "Version": "2012-10-17", "Statement": [ { "Sid": "AthenaQuery", "Effect": "Allow", "Action": [ "athena:StartQueryExecution", "athena:GetQueryExecution", "athena:GetQueryResults", "athena:StopQueryExecution", "athena:GetWorkGroup" ], "Resource": "arn:aws:athena:us-east-1:111122223333:workgroup/primary" }, { "Sid": "GlueCatalogRead", "Effect": "Allow", "Action": [ "glue:GetDatabase", "glue:GetDatabases", "glue:GetTable", "glue:GetTables", "glue:GetPartition", "glue:GetPartitions" ], "Resource": [ "arn:aws:glue:us-east-1:111122223333:catalog", "arn:aws:glue:us-east-1:111122223333:database/cur_database", "arn:aws:glue:us-east-1:111122223333:table/cur_database/*" ] }, { "Sid": "CurBucketRead", "Effect": "Allow", "Action": ["s3:GetObject", "s3:ListBucket", "s3:GetBucketLocation"], "Resource": ["arn:aws:s3:::acme-cur-exports", "arn:aws:s3:::acme-cur-exports/*"] }, { "Sid": "AthenaResultsReadWrite", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:AbortMultipartUpload", "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": ["arn:aws:s3:::acme-athena-results", "arn:aws:s3:::acme-athena-results/*"] }, { "Sid": "STSAccess", "Effect": "Allow", "Action": "sts:GetCallerIdentity", "Resource": "*" } ]}KubeOn GPU optimizer
Components that can change a cluster, only where you install them
The GPU optimizer has its own Helm release, and the KubeOn controller for automated savings is off by default with one role per feature. Clusters with neither are never changed by KubeOn.
- Installed only in clusters you choose, as its own Helm release, separate from the read-only agent
- Acts only on the node pools and namespaces a policy names; everything else is out of reach
- Hard limits per policy: minimum and maximum nodes, monthly cost, GPU-hour price
- Disruptive steps such as drains and repartitions run only in the policy's change window
- Evicts through the Eviction API, so PodDisruptionBudgets are always respected
- No access to Secrets or ConfigMaps outside the GPU Operator config it manages
- Pulls policies and approvals from the hub over HTTPS; the hub never connects into your cluster
- Every action in the audit log with the policy that allowed it, and a one-click revert where the change can be undone
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: kubeon-gpu-optimizerrules: # Read what it optimizes - apiGroups: [""] resources: [nodes, pods, namespaces] verbs: [get, list, watch] - apiGroups: [apps] resources: [deployments, statefulsets, replicasets] verbs: [get, list, watch] - apiGroups: [policy] resources: [poddisruptionbudgets] verbs: [get, list, watch] # Label, taint and cordon GPU nodes; drain through the Eviction API - apiGroups: [""] resources: [nodes] verbs: [patch] - apiGroups: [""] resources: [pods/eviction] verbs: [create] # Add and remove GPU capacity through Karpenter - apiGroups: [karpenter.sh] resources: [nodepools, nodeclaims] verbs: [get, list, watch, create, patch, delete] # Its own policies, synced from the hub, and their status - apiGroups: [gpu.kubeon.io] resources: [gpupolicies, gpupolicies/status] verbs: [get, list, watch, create, update, patch, delete]---# Created only in namespaces named by a ScaleToZero policykind: Rolerules: - apiGroups: [apps] resources: [deployments/scale, statefulsets/scale] verbs: [get, patch]---# In the gpu-operator namespace, for time-slicing onlykind: Rolerules: - apiGroups: [""] resources: [configmaps] resourceNames: [time-slicing-config] verbs: [get, patch]Data
What the agent and hub read, and what they never touch
Collected
- Node names, instance types, capacity, zones and provider IDs
- Namespace, workload and pod names, labels and owner references
- CPU and memory requests and measured usage per pod
- PersistentVolume and PVC sizes, classes and status
- Service types and load balancer hostnames
- Billing line items from your export: cost, usage type, resource ID
Never collected
- Secrets, ConfigMaps or environment variables
- Container images, logs or application data
- Request payloads or network packet contents from your clusters
- Credentials of any kind from your clusters
- Personal data about your customers, unless gateway prompts you choose to log contain it
UnitVane AI gateway
Model traffic, handled with care
The gateway sits in the path of your prompts, so it records as little as it can and keeps credentials out of your applications.
Metadata by default
Tokens, route, latency, cost and status are recorded. Bodies are logged only on routes with logging on; routes with the cache on keep responses until the cache TTL ends.
Email, phone and card masking
Email addresses, phone numbers and card numbers are masked before a request leaves the gateway. Names and other free text are not detected.
Credentials kept out of app code
Provider keys never sit in app code or gateway keys. UnitVane stores them encrypted and uses them only to call your provider.
Region pinning
Routes, fallback included, can be held to the providers and regions you choose.
Retention
How long data is kept
UnitVane keeps source data only as long as allocation and savings need it, and keeps the costs derived from it for the life of the workspace. These are the defaults.
On first connect, UnitVane reads 180 days of billing history and allocates it. The raw rows leave the hub after 90 days; the allocated costs stay for the life of the workspace. Whether evaluation data is deleted or kept when an evaluation ends is agreed before it starts.
| Data | Kept | When it expires |
|---|---|---|
| Source dataWhat the agent and your billing export deliver. UnitVane keeps it only as long as allocation and savings need it. | ||
| Cluster snapshotsOne compressed file per hourly agent run: nodes, pods, volumes and their usage | Kept: 14 days | Deleted by the snapshot bucket's lifecycle rule. Until then, a failed ingestion can be retried. |
| Pod usage samplesCPU and memory usage per pod from each agent run | Kept: 30 days | Deleted. Container rightsizing reads the last 7 days and node sizing the last 14. |
| Billing rowsLine items read from your billing export | Kept: 90 days | Deleted from the hub. The costs allocated from them stay, and your export remains the record: a billing backfill re-reads up to 366 days of it. |
| Athena query results (AWS)Results of the billing queries UnitVane runs | Kept: 14 days | Deleted. |
| Derived dataThe cost information UnitVane computes from the source data. | ||
| Allocated costs and reportsCost per pod, namespace, team, label and day, with shared, idle and unallocated lines, and saved reports | Kept: Life of the workspace | Kept after the raw rows are gone. Late data is repaired automatically for the last 7 days. Admins and platform engineers can re-run allocation up to 92 days back from Data health; a day whose billing rows have left the hub (after 90 days by default) needs a billing backfill first. |
| Assistant usage ledgerEvery Ask KubeOn call: user, feature, model, tokens and estimated cost | Kept: 365 days | Deleted. |
| UnitVane AI gatewayWhat the gateway keeps about the requests it routes. Bodies are not logged unless you turn logging on for a route, and routes with the cache on keep responses until the TTL ends. | ||
| Request metadataTokens, route, target, latency, status and cost of each request | Kept: 90 days by default | Deleted. |
| Prompt and response bodiesThe content of requests and responses | Kept: Not logged by default | Written to the log only on routes where you turn logging on. Cached responses are covered in the next row. |
| Cached responsesResponses on routes with the cache on | Kept: Until the route's cache TTL ends | Expire with the TTL. |
| At your model providerRequests sent to Azure OpenAI, Amazon Bedrock or Google Vertex AI | Kept: Set by your provider account | Requests go to your own provider account, so its data retention terms apply. |
Controls
How the hub is hardened
Network
- Internal load balancer by default, with an allow-list of private ranges
- TLS 1.3 policy on the load balancer, HTTP redirected to HTTPS
- Optional CloudFront with AWS WAF: IP allow-list, managed rules, rate limits
Application
- The hub refuses to start without a signing secret, or without KMS outside development
- Short-lived access tokens; lockout after 5 failed sign-ins for 15 minutes
- Strict security headers on every response
Secrets
- Secrets in your secrets manager, never in task definitions
- Database password generated and rotated by the cloud provider
- Integration credentials encrypted at rest
Build and release
- Trivy image scans, gitleaks secret scanning, dependency audits
- Checkov and tflint on every Terraform change
- Non-root images, pinned chart versions
Compliance
Inside your compliance boundary
UnitVane runs every product for you and reads your billing exports in place, with read-only access you can review. We provide the architecture, data flow, permission lists and answers to your security questionnaire during evaluation. For data residency in the EU, see data sovereignty.
Responsible disclosure
Report a vulnerability
Email security@unitvane.com with the details. We acknowledge reports within two business days and keep you updated until the issue is fixed.
Bring your security team to the demo.
We walk through the agent's permissions, the data flow and the hub's controls on your architecture.