wasmCloud backing services
In 5.3.6, tenant developers can request independent Redis and NATS instances through di-framework wasmcloud service. The shared @di-framework/platform package installs their Kubernetes APIs, provisions the backends, and projects connection configuration for bindings.
What changed in 5.3.6
Resource | Scope | Purpose |
|---|---|---|
| Cluster | Administrator-owned provider, sizing defaults, limits, and tenant visibility. |
| Tenant namespace | Request an independent |
| Tenant namespace | Associate a binding name with a service and create protected host configuration. |
All three use apiVersion: platform.di-framework.dev/v1alpha1. The release adds their installation, reconciliation, tenant RBAC, admission policies, quotas, and backend network policies, together with the service create, list, get, delete, and classes commands.
For tenant alpha, requests and binding projections live in di-tenant-alpha. Backend Deployments, Services, and connection Secrets live in di-runtime-alpha. A service named stock gets resources named di-bs-stock and a connection Secret named di-bs-stock-conn.
Prepare the platform and target
Use @di-framework/platform 5.3.6 and the wasmCloud CLI extension from the same release. Updating the application packages alone does not update the platform controller or CRDs. An administrator must update the platform package and apply the existing Pulumi stack. For an extension-managed project:
Then run di-framework wasmcloud platform deploy local --yes from the workspace root. Keep the existing project, backend, stack, and tenant declarations. For a kube-managed installation, select the platform package through kube's existing instance; avoid creating a second stack for the same cluster.
The administrator must have declared the tenant and granted the caller a developer membership. A User declaration does not issue a kubeconfig; credential issuance remains an administrator operation. See tenants and users.
Service commands use kubectl with the credentials resolved from the selected di-framework.deploy.toml target. They create custom resources through the Kubernetes API; the platform controller provisions the backend. For an external target, use the caller's tenant kubeconfig:
The target schema includes a registry even when running only service commands. Service commands do not publish an application image. --target, --namespace, and --context select the connection; omitting --namespace uses the target's namespace. With a managed target, explicitly select the tenant namespace if the platform outputs point to the platform namespace.
Create and inspect a service
Capability | Default class | Provider | Default sizing |
|---|---|---|---|
|
| Redis |
|
|
| NATS with JetStream |
|
Names contain lowercase letters and digits separated by hyphens, start with a letter, and have at most 40 characters. create requires a type and --name; it rejects an existing name. Use --class, --memory, --cpu, and --storage to request class-approved settings:
--wait polls the Ready condition every two seconds; its default timeout is 120 seconds. Without it, successful creation means the API accepted the request. get and list report readiness and endpoint summaries. A failed class lookup or invalid sizing reports Failed; a Deployment that is still starting reports Provisioning. Fix the request or platform configuration and let the controller retry.
classes attempts to read cluster classes and falls back to the built-in defaults when none are returned or the query fails. Tenant roles do not grant cluster-wide class access. A fallback listing does not prove those classes are installed or that the caller can use them.
Class configuration and limits
Platform installation seeds keyvalue-redis and messaging-nats by default. The Pulumi seedDefaultBackingClasses setting disables automatic seeding when false; backingServiceClasses supplies named class declarations that replace matching defaults or add classes. Classes support AllTenants and SelectedTenants visibility; the latter uses allowedTenants.
The 5.3.6 tenant admission policy accepts only the two approved default class names. Adding another class does not make its name usable by tenant requests. Administrators can adjust the existing classes' sizing and visibility while retaining their approved names.
Default class bounds are 64Mi–2Gi memory, 50m–2 CPU, and 256Mi–20Gi for the storage parameter. Requests must also fit the tenant's CPU and memory budget. Tenant resource settings backingServices and serviceBindings cap custom-resource counts, with defaults of 10 and 40. Runtime ResourceQuota also covers compute and declares a 50Gi storage-request budget.
Backends currently use node-local hostPath storage. The storage parameter is validated, but it does not allocate a PVC or enforce a filesystem size limit. The storage-request quota does not limit bytes written to those host paths.
Project a binding
Create a ServiceBinding in the same namespace as its BackingService. Save this as stock-binding.yaml and apply it with the same kubeconfig used by the target:
There is no ServiceBinding create command in 5.3.6. The controller checks that the referenced service exists in the same namespace, matches the capability, and is Ready with an endpoint. Unresolved or conflicting bindings report Failed and are retried on later reconciliations.
The controller creates di-binding-<bindingName> in the tenant namespace:
Capability | ConfigMap keys |
|---|---|
|
|
|
|
When the backend connection contains supported credential fields, they are projected into a separate di-binding-<bindingName>-creds Secret. Default Redis/NATS provisioning does not configure backend authentication, so that Secret may be absent. CR status contains references and readiness, never credential values.
Multiple ServiceBindings can share a bindingName when they agree on serviceName and capability. The controller retains the shared projection while any matching binding remains, including when the elected owner is removed. Deleting the last binding removes its projection; it does not delete the BackingService. Endpoint and credential changes are reconciled into the projection.
Workload integration boundary
The projected configuration is intended for a named wasmCloud host interface. This fragment shows the host-side contract for stock; it is not a complete deployable workload:
A messaging entry uses package: messaging and its own binding name. Subscription options stay in the workload's inline config.
Automatic application/decorator-to-ServiceBinding wiring is not included in 5.3.6. Creating the service and binding does not modify an existing workload or change its guest imports. The QuickJS native binding path still emits unnamed provider requirements. End-to-end named-backend deployment wiring is tracked in issue #455.
The Kubernetes ServiceBinding resource is also separate from the in-process @ServiceBinding decorator.
Tenant restrictions
The tenant is the authorization boundary. Developer memberships can create, update, and delete BackingServices and ServiceBindings in their tenant namespace; viewers can read them. Classes remain administrator-owned. workloadName on a ServiceBinding is descriptive and does not grant per-workload access; a Redis prefix is a key-naming convention, not authorization.
Kubernetes admission control checks requests before accepting them. Platform policies enforce ownership labels, approved classes, and same-namespace service references. Tenant workloads may use restricted wasi HTTP/config interfaces and wasmcloud keyvalue/messaging interfaces with controller-managed references. Arbitrary backend URLs, user-supplied backend references, and host-volume mounts are rejected. Stock keyvalue and default NATS retain transitional exceptions; the stock ConfigMap cannot select a messaging backend.
Tenant users cannot mutate the reserved di-bs-*, di-binding-*, and di-tenant-stock ConfigMaps/Secrets. Backend network policies allow ingress from the tenant hostgroup and require an enforcing network-policy engine. Tenant developers still have Secret read access and runtime pod port-forward access, so this does not isolate credentials or backends from other developers within the same tenant. Shared hosts remain disabled.
Deletion and existing data
spec.deletionPolicy defaults to Retain:
Retain: scale backend Deployments to zero and wait for the observed shutdown before releasing the service finalizer. Backend resources and hostPath data remain.
Delete: request removal of owned Deployments, Services, and connection Secrets, then release the finalizer. This does not erase hostPath data or wait for all resource deletion to finish.
Tenant suspension scales backends down and reports Suspended. Do not treat service delete as a data purge or a check that no application still depends on the service. Deleting a binding revokes its projected configuration; workload rollout and data migration are separate operations.
Application NATS instances use di-bs-<name>. The fixed di-nats is the runtime data plane and is managed by the tenant controller. Existing warehouse di-redis/di-tenant-stock resources remain transitional; 5.3.6 does not migrate their data into newly requested services. Full retention/cleanup and warehouse migration are tracked in issue #453 and issue #456.
Troubleshooting
Result | Check |
|---|---|
| The target kubeconfig, namespace, and tenant membership. |
| Inspect the existing service or choose a new name. |
| Inspect service conditions, class visibility/sizing, and Deployment readiness; increase |
| Read the service's failure condition and correct the request or platform configuration. |
Binding reports | Check same-namespace service existence/readiness, matching capability, and conflicting bindings with the same |
Resource name cannot be adopted | Existing resources belong to another service UID; inspect retained resources before reusing a deleted service name. |
Dedicated PostgreSQL (upcoming release)
The upcoming framework release adds the postgres capability and the default postgres-dedicated class. Each BackingService owns one PostgreSQL 18 instance, one PVC, and application credentials. Defaults are 1Gi storage, 512Mi memory, and 250m CPU. Multiple applications may share a service; distinct services have separate databases, volumes, and passwords.
Update the platform package and apply its existing Pulumi stack before using these APIs. Update the application CLI extension and @di-framework/wasmcloud together. Managed named imports require @di-framework/componentize-qjs 0.4.4-di.3 or later; the CLI installs the compiler dependency.
Create and bind
Declare the bindings in src/bindings.ts (or the project's configured bindings file):
Deploy with di-framework wasmcloud deploy --target alpha. The CLI validates the same-namespace references, creates deterministic ServiceBindings for that workload, and waits for their readiness before applying the WorkloadDeployment. Inferred workload members use the same binding discovery. Obsolete associations are removed after a successful rollout and when a workload is destroyed. Another workload using the same binding keeps the shared projection alive. A shared binding name must refer to the same service and capability throughout the tenant.
Each binding imports its own <binding>-query and <binding>-prepared interfaces; PostgreSQL types remain shared. Each named host interface references the protected di-binding-<binding>-creds Secret containing its complete connection URL. The controller keeps administrator credentials exclusively in the runtime namespace. The application connects to database app as its non-superuser owner app.
serviceName currently supports PostgreSQL only. It cannot be combined with secretFrom, configFrom, or config. Managed binding names are DNS labels of at most 54 characters; service names are at most 40. Existing decorators without serviceName retain their existing configuration behavior.
Storage and readiness
Managed local platforms install Rancher Local Path Provisioner v0.0.34, with data under /var/lib/k0s/di-postgres inside the persistent k0s Docker volume. Existing clusters need a working default StorageClass, or an administrator can set spec.storageClassName on postgres-dedicated before creating services:
A provisioned PVC keeps its selected StorageClass even if the class definition changes. Storage cannot shrink. Expansion requires a StorageClass with allowVolumeExpansion: true; local-path does not support expansion. Requested capacity participates in Kubernetes quota accounting, but local-path does not enforce that capacity on disk. CPU and memory requests and limits apply to each instance. Backend NetworkPolicy permits tenant hostgroup ingress.
Readiness authenticates to the application database and runs SELECT 1 after idempotent startup bootstrap. StoragePending points to PVC/provisioner or scheduling problems; Initializing waits for bootstrap; InitializationFailed points to runtime pod logs. A CredentialsMissing failure requires restoring the original runtime Secret. The controller never generates replacement passwords for an existing PVC. Suspending the tenant stops the instance and preserves its PVC and credentials; resuming uses both again.
Delete and recover
Deletion blocks while any ServiceBinding references the service. Its status lists the blocking associations, existing connections remain available, and new associations are refused. Remove the binding from application source and redeploy, or destroy the consuming workload. Remove manually created ServiceBindings with kubectl delete servicebinding <name> -n di-tenant-alpha.
After all associations are removed, the controller stops PostgreSQL and waits for its pods to terminate:
Retain (default): removes serving resources, keeps the PVC and both runtime credential Secrets for administrator recovery.
Delete: removes serving resources, credentials, and the PVC, and waits for completion before releasing the BackingService finalizer. Physical volume reclamation follows the StorageClass reclaim policy.
Select deletion policy at creation with --deletion-policy Delete, or update it before deletion:
Persistent resource names include a hash of the BackingService UID. Recreating orders creates fresh storage and credentials, so it cannot silently inherit a retained database. Administrators can find retained resources by service label:
For recovery, identify the retained PVC and matching -auth Secret from the same UID generation. Mount that PVC into an administrator-managed PostgreSQL 18 recovery pod at /var/lib/postgresql, set PGDATA=/var/lib/postgresql/18/docker, and use envFrom.secretRef.name with the retained -auth Secret. Use pg_dump -U app -d app with PGPASSWORD=$APP_PASSWORD inside that pod to export data, then restore into a new service. Do not mount the retained PVC concurrently with another PostgreSQL instance. Back up recovery credentials securely alongside database backups.
For example, replace RETAINED_PVC and RETAINED_AUTH_SECRET below with names from the same retained generation, then apply this recovery pod:
Restore orders.dump with pg_restore --no-owner into the new service's app database using its application credentials. Keep the retained PVC and credentials until the restored data has been verified.
Retention is not a backup: deleting the runtime namespace or the underlying platform volume can still destroy retained data and credentials. Initial support provides one instance with authenticated internal connections. Automated backups, replication, TLS provisioning, major-version upgrades, and credential rotation remain future work.