Hosting contract

What Episodic supplies, and what Nile Valley expects

The deployable contract between the application and the platform that runs it. A working chart is not a deployed environment, and this page keeps the two apart.

The runtime contract

Six facts the platform depends on. Change any of them and a deployment stops working.

Episodic runtime contract
ItemValueNote
Entrypoint granian episodic.api.runtime:create_app_from_env --interface asgi --factory The production HTTP process. Granian is the ASGI server; Falcon is the application.
Port 8080 Exposed by the image and named http in the chart.
Required configuration DATABASE_URL The runtime refuses to start without it.
Liveness probe GET /health/live Reports that the process has booted.
Readiness probe GET /health/ready Checks database connectivity and returns 503 on failure, so the platform withholds traffic.
Process user Non-root, uid and gid 10001 With a read-only root filesystem and all capabilities dropped.

Readiness returning 503 is deliberate rather than a failure state. It is how the application tells the platform to withhold traffic while a dependency is unreachable, without being restarted for it.

Who owns what

The boundary is the point of this page.

Application and platform ownership
ConcernOwned byDetail
Container image Episodic Builds a wheel in a builder stage, installs it into a slim runtime stage, and declares its own HEALTHCHECK.
Health semantics Episodic Liveness and readiness meanings live in the domain; the Falcon adapter maps them to status codes.
Helm chart and application values Episodic charts/episodic renders Deployment, Service, ConfigMap, ServiceAccount, and optional Ingress, ExternalSecret, and PodDisruptionBudget.
Kubernetes cluster and ingress Nile Valley Shared cluster provisioning, Traefik ingress, certificates, and DNS.
GitOps reconciliation Nile Valley FluxCD reconciles desired state from the GitOps repository. Application automation publishes an immutable image and desired Helm state; it does not deploy workloads directly.
Secret material Nile Valley Secret sync from the shared Vault appliance. Git holds references, never secret values.
Database and cache Nile Valley CloudNativePG and the Valkey operator supply the shared data services.

Chart values

Names as the chart spells them, following the Nile Valley example chart conventions.

Selected values from charts/episodic
ValuePurposeDefault
config Non-secret environment variables rendered into a ConfigMap. Empty
existingSecretName Reuses an operator-managed Kubernetes Secret rather than creating one. Unset
secretEnvFromKeys Maps environment variable names to Secret keys. Object entries, so an explicit optional: false survives. Empty
allowMissingSecret Fallback optional flag applied to Secret keys that do not state their own. false
externalSecret Renders External Secrets Operator resources instead of consuming a Secret directly. Disabled
container.livenessProbe Defaults to GET /health/live on the http port. Enabled
container.readinessProbe Defaults to GET /health/ready on the http port. Enabled
ingress Optional. The platform supplies the controller, certificates, and DNS. Disabled
podDisruptionBudget Optional. Off unless a deployment needs availability guarantees. Disabled

The pod runs as a non-root user with a read-only root filesystem, all capabilities dropped, privilege escalation disabled, and the default seccomp profile. Those are chart defaults rather than deployment-time choices, so a permissive deployment takes deliberate effort.

No credential, database URL, Vault path, cluster identifier, or production hostname appears anywhere on this site. Values shown here are structural.

How a change reaches a cluster

Continuous integration does not deploy anything. It publishes intent.

  1. A change merges and application automation builds an image.
  2. The image is published to the registry under an immutable tag.
  3. Automation writes the desired Helm state - chart version and values - into the GitOps repository.
  4. FluxCD, running inside the cluster, observes the change.
  5. Flux reconciles the declared state, pulling the image and applying the release.
  6. Secret material is synchronised into the cluster from the shared Vault appliance. Git holds references, never values.

The distinction matters for incident response. Nothing outside the cluster holds credentials for it, and rolling back is a commit rather than a pipeline rerun.

Exercising it locally

The same chart and image, in k3d, before any of it reaches the platform.

Local Kubernetes preview
make local-k8s-up      # create or update the preview
make local-k8s-status  # inspect it
make local-k8s-logs    # read its logs
make local-k8s-down    # tear it down

The targets drive a Cyclopts command that provisions a k3d cluster and installs the chart. Running it is the cheapest way to find out whether a change to the runtime contract breaks the deployment, and it needs no access to any shared environment.

See the component catalogue · Prepare the checkout first