1. Start here

1.4 The application contract

Requirements: R8, R9, R11, R13, R14, R15, R16, R18, R21, R22, R23, R24, R26, R29, R31, R33.

What an application team has to do, and nothing more, to run on the platform in any cloud. Everything here is enforced by admission policy, a CI check or the onboarding, and each line links to the section that explains it; the reference application of section 0.3 meets all of it and contains no Azure name.

Manifests

Plain Kubernetes, in the team’s own repository, one directory per application and one per cell for what differs there (section 3.3). Only GA Kubernetes APIs, the Gateway API, External Secrets and Cilium network policy objects; no cloud CRDs (section 1.3).

The application Why Enforced by
Runs as a Deployment with replicas or HPA minReplicas of at least 2 (or 0 when switched off), topologySpreadConstraints over nodes, RollingUpdate with maxUnavailable: 0, and its own PodDisruptionBudget with maxUnavailable of at least 1 A release or a node drain never takes it down (section 3.4) Admission; the PDB by CI
Runs work that serves no request as a Job or CronJob (backoffLimit, activeDeadlineSeconds, ttlSecondsAfterFinished, concurrencyPolicy: Forbid) or, for a queue consumer, as a Deployment with at least 1 replica scaled on queue length and a graceful shutdown longer than its longest message; such a workload has no route and lives in the application’s home cell in both exposures Work that must not repeat runs once per environment; a cell drain finishes or restarts it (non-serving workloads) Admission (the rollout rules apply to serving workloads only); the placement
Declares readiness and liveness (and startup) probes and resource requests; limits stay in Git, requests are resized in place by the platform’s VPA unless the pod carries platform/rightsizing: off HPA, the autoscaler and the N+1 sizing work from them (section 2.3) Admission; the VPA is platform-maintained
Shuts down gracefully: a preStop delay and terminationGracePeriodSeconds longer than the gateway’s drain Connections finish during a rollout or drain (section 3.4) Review
Uses an image from the environment’s registry, by digest once promotion verifies signatures; builds arm64 or multi-architecture images when it chooses those nodes No image reaches acc or prd unsigned (section 3.2, section 2.7) Admission (allowed images, Image Integrity); CI checks the image index against the labels
Builds its image from the platform’s base image for its runtime with the platform’s build workflow, from a main that always builds and passes its tests; accepts that the platform rebuilds and promotes it when the base is patched Every application is patched on the platform’s timetable, gated by the team’s tests alone (section 1.5) The build workflow; the registry refuses an image past its deadline
Meets the restricted pod security standard, with readOnlyRootFilesystem: true (writable paths as emptyDir), automountServiceAccountToken: false, no hostNetwork, no privileged containers, no PersistentVolumeClaim in a stateless cell A compromised pod stays a pod (section 2.7) Admission
Puts the application’s Deployment, Service, HTTPRoute and ExternalSecrets in its namespace; writes no Ingress, Gateway, LoadBalancer, NodePort or egress policy out of the cluster The zone’s gateway is the platform’s (section 4.1) Admission

Traffic in and out

The application Why Enforced by
Publishes itself with an HTTPRoute on the zone’s Gateway, with hostnames in the zone’s domain (<name>.<zone>.<env>.<domain>); TLS, the certificate and DNS are the platform’s HTTPS only, HTTP/2 only and TLS 1.3 only at the edge (section 4.1) Admission (Gateway rules)
Serves HTTP/2 cleartext (h2c) on its route port, and declares appProtocol: kubernetes.io/h2c on that Service port; keeps HTTP/1.1 for the kubelet’s probes until Kubernetes 1.38 No HTTP/1.1 between a gateway and a pod (section 4.1) Admission (h2c-upstream) and CI; the onboarding checks the image with curl --http2-prior-knowledge
Answers GET /healthz with 200 only while it can serve, behind the gateway with its host name Traffic Manager and the synthetic probes leave out a cell whose probe fails (R24) The probes
Expects no egress by default. A new external destination is a request: the egress agent proposes the Firewall rule from the denied flows and the team confirms it, or the team opens the pull request itself (section 5.1); the application writes no egress policy Egress only to named destinations (R29) The hub Firewall; admission (app-egress)
Listens from outside its namespace only through the zone’s gateway, HTTPS on 443 to its route; between its own pods it uses any protocol and port One way in for every client, also the zone’s other applications (R22) Cilium default deny (inbound only from the namespace and the zone’s gateway); admission (no LoadBalancer, NodePort or Ingress)
Reaches other applications, of its zone or another, only through their published names with a rule in its own rule file; another zone’s pods or nodes, a Key Vault, the instance metadata service and the stateful cluster of another zone never Isolation (section 2.4, section 2.6) Cilium default deny, the Firewall, the NSGs

Identity, secrets and data

The application Why Enforced by
Authenticates to every Azure service with its workload identity (id-<zone>-<app>, the ServiceAccount’s azure.workload.identity/client-id annotation); its configuration holds endpoints and client IDs only No keys, connection strings or SAS tokens; local auth is disabled on the services (section 2.5) Azure Policy on the services; the onboarding creates the identity and roles
Takes a secret only where the target cannot use Entra ID, as an ExternalSecret against the namespace’s SecretStore key-vault, naming a secret under its own prefix <namespace>--; mounts it as a volume; never calls Key Vault itself Secrets reach a pod only through External Secrets Operator, separated per namespace (section 2.5) Admission (ESO rules); Key Vault ABAC; no vault role on the application’s identity
Keeps its data in a PaaS service over its open protocol (PostgreSQL, MySQL, Redis, MongoDB, Kafka, S3-compatible storage) and the common subset of its features; a cloud-specific service or extension is a documented exception at onboarding Portability of the contract (section 1.3) Onboarding review
Changes its data and APIs backwards compatibly - expand → migrate → contract over separate releases Two versions run at once during a release (section 3.4) Review; the release train’s SLO gates
Asks for the stateful cluster only with a documented exception (why neither a stateless cell nor a PaaS service fits, owner, exit plan); there it terminates TLS 1.3 itself on an internal load balancer The stateful cluster is the last option (section 3.1) The exception label on the namespace; admission

Choices the application makes with labels

Pod label Values Effect
platform/arch amd64 (default), arm64, multi Which nodes; multi lets the platform pick the cheapest (section 2.3)
platform/capacity on-demand (default), spot Spot nodes when available, on-demand otherwise; only for workloads that tolerate losing replicas with 30 seconds’ notice
platform/rightsizing off The VPA only recommends; the baseline in Git still moves by pull request

The platform turns the labels into node selection; an application that sets selectors, tolerations or affinities for nodes itself is refused.

Observability

The application Why Enforced by
Logs JSON to stdout; emits OpenTelemetry (OTLP) to the endpoint the platform injects (OTEL_EXPORTER_OTLP_ENDPOINT) and propagates W3C traceparent; logs carry trace_id One Grafana Cloud stack per environment, node-local collection (section 6.2) The injected variables; Beyla instruments without code
Logs no secret, token or personal data that the environment’s users may not see Every user of a stack sees all of its data (section 6.2) Alloy’s redaction is a safety net only
Keeps its SLO objectives, dashboards and alerts in apps/<app>/observability/ next to its manifests Default SLOs come from the gateway; the team sets the targets The IaC pipeline applies them

Onboarding

An application starts with the request template (section 6.1): the zone, the team’s Entra ID groups for view, edit and admin, the owner labels platform/team and platform/cost-center and the contact annotation (section 2.6), the Azure services it needs and whether it needs a secret, and, for an internal application, its home cell (section 3.1). The onboarding agent turns it into the namespace, the identities, the roles and the Flux configuration; the team never touches cluster-scoped objects. The journey around the contract - onboarding, migrating an existing application and operating it in production - is section 1.5. Cost is reported per team from the labels, and a budget alerts the owners.

OneK8s – a design under review; country codes and IP ranges are examples.Source of this pageComments and issues