| Status | Active |
|---|---|
| Owner | HyperFleet Team |
| Last Updated | 2026-03-23 |
- Overview
- 1. Key Naming Conventions
- 2. Values.yaml API Surface
- 3. Chart Versioning Strategy
- 4. Default Security Posture
- 5. Secret Management Pattern
- 6. Configuration Reload Strategy
- 7. NOTES.txt
- 8. Chart Testing
- 9. Deprecation and Migration Pattern
- 10. Standard Labels and Annotations
- 11. Broker Configuration Pattern
- 12. Image Configuration
- Enforcement
- References
This standard defines conventions for Helm charts across all HyperFleet component repositories (API, Sentinel, Adapter). It ensures consistent naming, structure, security posture, and operational patterns across charts. All HyperFleet charts MUST follow these conventions.
All keys in values.yaml MUST use camelCase.
# Good
broker:
googlepubsub:
projectId: "my-project"
createTopicIfMissing: false
deadLetterTopic: ""
adapterConfig:
hyperfleetApi:
baseUrl: http://hyperfleet-api:8000
# Bad
broker:
googlepubsub:
project_id: "my-project"
create_topic_if_missing: false
dead_letter_topic: ""
adapterConfig:
hyperfleet_api:
base_url: http://hyperfleet-api:8000Config files generated by templates (e.g., config.yaml, broker.yaml) MUST use snake_case for property names, as required by the Configuration Standard.
# Template output (snake_case) from camelCase values input:
broker:
type: googlepubsub
googlepubsub:
project_id: {{ .Values.broker.googlepubsub.projectId | quote }}
create_topic_if_missing: {{ .Values.broker.googlepubsub.createTopicIfMissing }}Environment variables MUST use UPPER_SNAKE_CASE with the HYPERFLEET_ prefix where applicable, per the Configuration Standard.
Generated Kubernetes resource names (ConfigMaps, Services, etc.) MUST use kebab-case, derived from the Helm release name and chart name via fullname helper.
Helper templates MUST be prefixed with the chart name in kebab-case:
{{- define "hyperfleet-adapter.fullname" -}}
{{- define "hyperfleet-sentinel.labels" -}}
{{- define "hyperfleet-api.selectorLabels" -}}
Every HyperFleet chart MUST expose the following top-level sections:
| Section | Purpose |
|---|---|
image |
Container image configuration |
replicaCount |
Number of replicas (ignored when HPA enabled) |
resources |
CPU/memory requests and limits |
securityContext |
Container-level security context |
podSecurityContext |
Pod-level security context |
serviceAccount |
ServiceAccount creation and naming |
podAnnotations |
Additional pod annotations |
podLabels |
Additional pod labels |
nodeSelector |
Node scheduling constraints |
tolerations |
Toleration rules |
affinity |
Affinity/anti-affinity rules |
podDisruptionBudget |
PDB configuration |
imagePullSecrets |
Image pull secrets for private registries |
nameOverride |
Override chart name in resource names |
fullnameOverride |
Override full resource name |
Charts SHOULD expose these when the component requires them:
| Section | When Required |
|---|---|
autoscaling |
Stateless components that support horizontal scaling |
broker |
Components that consume or publish events |
serviceMonitor |
Components that expose Prometheus metrics |
config |
Components with application-specific configuration |
observability |
Components with health/readiness probes and metrics (see Health Endpoints Standard) |
- Chart version (
versioninChart.yaml) MUST follow SemVer 2.0. - Chart version and
appVersionMUST be coupled — both track the same git tag. When a component is taggedv1.5.0, the chart publishes withversion: 1.5.0andappVersion: "1.5.0". - The
build-helm-chart-oci-tatask derives the version from the git tag automatically, stripping thevprefix (e.g.,v1.5.0→1.5.0). - Bump major for breaking changes (values.yaml API, application API).
- Bump minor for new features.
- Bump patch for fixes.
Chart and application source live in the same repo and are tagged together. Independent versioning would require separate pipeline triggers, a version matrix, and tracking which chart versions are compatible with which app versions — complexity with no practical benefit for our team size and chart simplicity. See Helm OCI Distribution Design Section 6.
appVersionin source MUST be set to"0.0.0-dev"— it is a placeholder.- CI/release tooling stamps the real application version (e.g.,
1.5.0) at release time, matching the chartversion. appVersionMUST NOT be used as a fallback for the container image tag. Image tags come from git SHAs (dev) or semver (release) and are always set explicitly viaimage.tag.- Umbrella charts (e.g., in
hyperfleet-infra) MUST also useappVersion: "0.0.0-dev"since they do not build binaries.
All charts MUST ship with a restrictive security posture by default. Operators MAY relax these for specific environments.
podSecurityContext:
fsGroup: 65532
runAsNonRoot: true
runAsUser: 65532securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
seccompProfile:
type: RuntimeDefaultCharts MUST NOT default allowPrivilegeEscalation to true, readOnlyRootFilesystem to false, or retain any Linux capabilities.
- Sensitive values (passwords, tokens, API keys) MUST NOT be stored in ConfigMaps.
- Secrets SHOULD be referenced via
secretKeyRefin environment variables. - Charts SHOULD support an
existingSecretfield to reference pre-created Secrets. - Charts MUST NOT hardcode credentials in
values.yamldefaults (use placeholder values likeCHANGE_ME). - Production deployments SHOULD use external secret management (External Secrets Operator, Sealed Secrets, Vault).
# Good: Reference an existing secret
database:
external:
secretName: "my-db-credentials"
# Good: Environment variable from secret
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-secret
key: password
# Bad: Credential in ConfigMap or hardcoded default
database:
password: "my-password"Charts MUST use checksum annotations on the pod template to trigger pod restarts when ConfigMap content changes. This ensures configuration changes are picked up without manual intervention.
# In deployment.yaml pod template metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}Each ConfigMap that affects the pod SHOULD have its own checksum annotation. Use descriptive keys:
| Key | Purpose |
|---|---|
checksum/config |
Application configuration |
checksum/task-config |
Task/job configuration |
checksum/secret |
Secret content (when chart manages the Secret) |
Runtime hot-reload is NOT supported per the Configuration Standard. Pod restart is the reload mechanism.
Charts SHOULD include a templates/NOTES.txt file with post-install instructions. The content SHOULD include:
- How to check the deployment status
- How to access the service
- Links to documentation
Charts MUST include a test-helm target in their Makefile, per the Makefile Conventions.
The test-helm target MUST validate:
- Lint (
helm lint) - Chart syntax validation - Template render (
helm template) - Default values produce valid YAML - Key configuration variants - Template renders for each major feature toggle (e.g., autoscaling enabled, PDB enabled, ServiceMonitor enabled)
Template tests SHOULD assert on rendered output content, not just exit code:
# Good: Assert content
OUTPUT=$(helm template test charts/ --set image.registry=quay.io)
echo "$OUTPUT" | grep -q 'kind: ServiceMonitor' || { echo "FAIL: ServiceMonitor not found"; exit 1; }
# Bad: Only check exit code
helm template test charts/ --set image.registry=quay.io > /dev/nullWhen renaming or removing values.yaml keys:
- MUST bump chart major version for breaking changes.
- SHOULD add a
failmessage in templates to guide users:
{{- if .Values.broker.googlepubsub.project_id }}
{{- fail "broker.googlepubsub.project_id has been renamed to broker.googlepubsub.projectId (camelCase). Please update your values." }}
{{- end }}- SHOULD document the migration in
Chart.yamldescription or a CHANGELOG.
Charts MUST include these Kubernetes recommended labels on all resources via a shared labels helper:
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}The app.kubernetes.io/version label MUST use .Values.image.tag with .Chart.AppVersion as fallback. This ensures the label reflects what is actually deployed — a git SHA in dev (e.g., abc1234) or a semver in release (e.g., 1.2.3). The Kubernetes recommended labels spec explicitly allows both revision hashes and semantic versions.
Deployment matchLabels and Service selectors MUST use a minimal, immutable subset:
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}Resources that are part of a larger deployment (e.g., ConfigMaps, sub-components) SHOULD include:
app.kubernetes.io/component: <component-name>Charts MUST support adding custom labels and annotations via values:
podAnnotations: {}
podLabels: {}Components that use the hyperfleet-broker library MUST follow a standard broker configuration structure.
broker:
# Broker type: googlepubsub or rabbitmq
type: ""
# Topic name (supports Helm templating)
topic: ""
googlepubsub:
projectId: ""
createTopicIfMissing: false
createSubscriptionIfMissing: false
subscriptionId: ""
deadLetterTopic: ""
maxOutstandingMessages: 1000
numGoroutines: 10
rabbitmq:
url: ""
queue: ""
exchange: ""
exchangeType: topic
routingKey: ""- All broker values.yaml keys MUST use camelCase.
- Generated
broker.yamlconfig file MUST use snake_case (matching the broker library's expected format). - The template MUST translate camelCase values to snake_case output.
broker.typeMUST be set explicitly. Templates MUST NOT attempt to infer the broker type from populated sub-keys.
Charts MUST use the following image configuration following the Bitnami/community convention:
image:
registry: CHANGE_ME
repository: CHANGE_ME
tag: "" # Required — no default. Set via --set image.tag=<version>
pullPolicy: Always| Field | Content | Example |
|---|---|---|
registry |
Registry host only | quay.io, registry.ci.openshift.org |
repository |
Org path + image name | openshift-hyperfleet/hyperfleet-api |
tag |
Version tag (git SHA or semver) | abc1234, v1.2.3 |
pullPolicy |
Image pull policy | Always, IfNotPresent |
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"image.tag MUST NOT fall back to .Chart.AppVersion. Since appVersion is "0.0.0-dev" in source, a fallback would silently attempt to pull a non-existent image instead of failing fast.
Charts MUST include a validation guard in _helpers.tpl that fails if image.tag is not set. The guard SHOULD be part of the chart's validateValues helper:
{{- define "<chart-name>.validateValues" -}}
{{- $registry := trim (toString .Values.image.registry) -}}
{{- if or (not $registry) (eq $registry "CHANGE_ME") -}}
{{- fail "image.registry must be set (e.g. --set image.registry=quay.io)" -}}
{{- end -}}
{{- $repository := trim (toString .Values.image.repository) -}}
{{- if or (not $repository) (eq $repository "CHANGE_ME") -}}
{{- fail "image.repository must be set (e.g. --set image.repository=openshift-hyperfleet/<component>)" -}}
{{- end -}}
{{- if not (trim (toString .Values.image.tag)) -}}
{{- fail "image.tag must be set (e.g. --set image.tag=abc1234)" -}}
{{- end -}}
{{- end }}The validateValues helper MUST be invoked at the top of deployment.yaml (or any template that renders a workload):
{{- include "<chart-name>.validateValues" . }}
apiVersion: apps/v1
kind: Deployment
...registryandrepositoryMUST default toCHANGE_MEto force explicit configuration.repositoryvalues MUST include the org path (e.g.,openshift-hyperfleet/hyperfleet-api).tagMUST default to empty string. It is a required field — there is no fallback.pullPolicySHOULD default toAlwaysduring development. For production with pinned semver tags, operators SHOULD override toIfNotPresentto avoid unnecessary registry pulls.
# When overriding, repository MUST include org path
# Good: registry=quay.io, repository=openshift-hyperfleet/hyperfleet-api
# Bad: registry=quay.io, repository=hyperfleet-api- CI:
helm lintandhelm templaterun viamake test-helmin CI pipelines. - Code Review: Reviewers MUST verify values.yaml naming conventions and security defaults.
- Migration: Existing charts that do not comply MUST be updated with deprecation
failmessages for renamed keys.