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.
| Item | Value | Note |
|---|---|---|
| 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.
| Concern | Owned by | Detail |
|---|---|---|
| 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.
| Value | Purpose | Default |
|---|---|---|
| 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.
- A change merges and application automation builds an image.
- The image is published to the registry under an immutable tag.
- Automation writes the desired Helm state - chart version and values - into the GitOps repository.
- FluxCD, running inside the cluster, observes the change.
- Flux reconciles the declared state, pulling the image and applying the release.
- 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.
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.