VM execution storage
Agent-backed VM commands/shells use full protected session recording and metadata audit. Persistence is optional: choose ephemeral history, externally managed PostgreSQL/S3, or bundled PostgreSQL/RustFS. Ordinary execution and elevation have separate permissions; selecting a storage mode does not grant either permission.
Implementation status: deployment/configuration, database migrations,
encrypted recording stores, protected playback and admission foundations are
implemented. Guest-authenticated transport, recording-spool integration and
execution runners remain pending. Provisioned services or accepted
configuration do not establish execution readiness. These examples do not enable
PlatformConfig.spec.vmExecution or expose a guest shell by themselves.
Modes
| Mode | Selection | History and dependencies |
|---|---|---|
| Disabled | Default execution.enabled: false
|
No execution configuration or storage services. |
| Ephemeral |
execution.enabled: true, neither persistent selector enabled |
No PostgreSQL/S3/PVC. Bounded encrypted temporary recording and process-local metadata; history may disappear on restart/replacement. |
| External | execution.storage.external.enabled: true |
Operator-managed PostgreSQL and S3-compatible store. No bundled storage installed. |
| Bundled | execution.storage.bundled.enabled: true |
PostgreSQL and RustFS subcharts with persistent volumes, internal Services and storage ingress NetworkPolicies. |
The external/bundled selectors are mutually exclusive. The same bundled selector controls both Helm dependencies, avoiding partially installed hybrid storage. Execution infrastructure requires authentication. Persistent failures must not fall back to ephemeral mode. Ephemeral mode currently requires one API replica and Recreate updates; persistent modes provide the foundation for distributed coordination. Losing temporary storage must terminate/fence its executions, not resume or rerun commands automatically.
The hybrid contract stores execution state, searchable audit/index metadata and coordination in PostgreSQL; encrypted recording chunks/manifests go to S3. Session metadata and client capability discovery must identify the selected durability. Ephemeral mode still records full input/output and enforces resource bounds; it does not promise retained or independently durable evidence.
Ephemeral profile
helm template kw helm/kube-workspaces -n kube-workspaces-system \
-f examples/execution-storage/ephemeral.yaml
The API gets a bounded emptyDir and a 512 MiB application recording budget by
default, with no persistent storage or persistent encryption key dependency.
The runtime generates an ephemeral key; discarded keys/history are unrecoverable.
Container restarts may leave ciphertext in emptyDir, but cannot be treated as
recoverable history with a new key. Resource exhaustion must fence admission/
input rather than silently stop recording. emptyDir/node lifecycle can remove
data earlier than configured retention.
External PostgreSQL + S3
Use examples/execution-storage/external.yaml. Pre-create its database, bucket,
credentials and recording-key Secrets in the release namespace. References:
| Secret | Keys / contract |
|---|---|
execution-postgresql |
database-url: PostgreSQL URL using a dedicated app role/database and sslmode=verify-full. |
execution-s3 |
access-key, secret-key: credentials restricted to the execution bucket/prefix. |
execution-recording-key |
key: base64 text encoding a random 32-byte recording master key. |
Set execution.storage.s3.endpoint, bucket and region for the provider. Custom
Secret key names and path-style requests are configurable. For a private S3 CA,
set execution.storage.s3.caSecret (key ca.crt); certificate validation remains
enabled. The mounted CA path can also be referenced by a PostgreSQL URL
sslrootcert parameter if both services use that CA. PostgreSQL client-certificate
mounts and workload-identity credentials are not yet provided by this profile.
External mode does not create buckets by default or manage credentials, database backups or object-store retention policies. Operators manage those services and permissions. SeaweedFS, RustFS, AWS S3 and other compatible providers use the same S3 interface; no provider-specific APIs belong in the guest/client protocol.
Bundled test profile
examples/execution-storage/bundled.yaml installs:
- CloudPirates PostgreSQL chart 0.21.2, using the upstream PostgreSQL image. A separate non-superuser application role/database is initialized once. The API receives app credentials, not the database administrator password.
- Upstream RustFS chart 1.0.2, standalone, with a 20 GiB data PVC and no public ingress or console enabled. The S3 endpoint is discovered from the subchart Service name, including fullname overrides.
Dependencies are pinned in Chart.yaml/Chart.lock and vendored for offline
rendering. RustFS is the initial bundled provider; managed SeaweedFS remains an
external S3 option. Bundled does not imply high availability or backups.
Create Secrets once, outside Helm values/Git. For example (generic installation namespace; these are operator commands, not install hooks):
kubectl -n kube-workspaces-system create secret generic execution-postgresql-admin \
--from-literal=postgres-password="$(openssl rand -hex 32)"
kubectl -n kube-workspaces-system create secret generic execution-postgresql-app \
--from-literal=CUSTOM_USER=execution --from-literal=CUSTOM_DB=execution \
--from-literal=CUSTOM_PASSWORD="$(openssl rand -hex 32)"
kubectl -n kube-workspaces-system create secret generic execution-rustfs \
--from-literal=RUSTFS_ACCESS_KEY="$(openssl rand -hex 16)" \
--from-literal=RUSTFS_SECRET_KEY="$(openssl rand -hex 32)"
kubectl -n kube-workspaces-system create secret generic execution-recording-key \
--from-literal=key="$(openssl rand -base64 32)"
Then render/review the bundled profile before the separately coordinated chart adoption. Existing authentication/IdP or local-account configuration still needs to be supplied in the installation’s other values files.
helm template kw helm/kube-workspaces -n kube-workspaces-system \
-f examples/execution-storage/bundled.yaml
The test profile explicitly opts into internal PostgreSQL/TCP and S3/HTTP via
execution.storage.allowInsecureTransport: true. Recording content must be
application-encrypted before it reaches those stores; this flag does not disable
TLS certificate validation on HTTPS endpoints. NetworkPolicies allow API ingress
to storage (and S3 peer traffic) only within the release namespace; the CNI must
enforce NetworkPolicy. For verified transport encryption use external endpoints;
the baseline bundled profile does not configure PostgreSQL server TLS.
Adjust postgresql.persistence.storageClass/size and
rustfs.storageclass.name/dataStorageSize to the cluster’s StorageClasses. Empty
class names use cluster defaults. PVCs are not backups: PostgreSQL claim-template
volumes are retained by StatefulSet defaults and the RustFS standalone PVC has
the upstream helm.sh/resource-policy: keep annotation. Namespace/PVC deletion,
storage failure or reclaim policies can still destroy data. Keep credentials
and the recording key with the retained data. Changing a Secret is not database
password rotation; initialization runs only on a new database volume.
For multiple releases, use distinct Secret names and bucket/prefixes. Switching modes/providers or replacing volumes does not migrate history. Drain active executions, copy/verify metadata and objects together, retain the corresponding keys, and only then change storage references. Do not reuse a data volume across incompatible database major versions or silently reinitialize missing history.
Retention, keys and readiness
Initial configurable defaults are 720 hours of recording and 8760 hours of metadata. Metadata retention must cover recording retention so replay/index and deletion evidence are not removed before their recording objects. Full input recording includes echo-disabled credentials: replay is a separate permission and must itself be audited. Content never belongs in ordinary application/debug logs.
Retained data must remain decryptable across API restarts; a persistent recording key is mandatory. The current configuration loads one key and derives its ID. Key-ring rotation/migration is still pending: do not replace that key while recordings encrypted with it remain. Back up keys separately with controlled access; neither PostgreSQL/S3 availability nor a chart install alone establishes recording health. Admission must verify both stores, the encryption key, write authorization and available recording capacity before any process starts.
Opt-in management issuer
The API now supports optional dedicated CA loading, shared enrollment-ledger
initialization and a protected-bootstrap import worker. The existing authenticated
GET /v1/executions/status reports issuer_ready separately from recording-store
availability. That bit does not advertise a guest execution capability or a public
enrollment endpoint.
The runtime opts in through KW_MANAGEMENT_ISSUER_ENABLED and requires mounted
CA certificate/chain, PKCS#8 private-key and explicit trust-root files, plus
KW_MANAGEMENT_BOOTSTRAP_NAMESPACE matching the controller’s protected namespace.
CA signing private keys are not generated automatically or placed in images/Helm values.
Credential/Secret mounts and runtime environment adoption remain separately
coordinated; the storage profiles above do not enable the issuer.
The current issuer requires external or bundled execution storage with the
shared PostgreSQL enrollment ledger. It rejects ephemeral/process-local issuance
state: after restart, a forgotten redeemed token could otherwise enroll a second
CSR. Ephemeral recording remains a storage option, but full guest execution in
that mode still needs a replica-safe alternative enrollment ledger and the other
guest prerequisites.
The worker watches only protected-namespace management bootstrap metadata and re-reads credentials before trusted import. It preserves committed CSR/certificate results across re-import, denies token rebinding/expiry extension, and withholds issuance when current identity, policy, authentication, CA trust/lifetime, recording or ledger prerequisites fail. Trust/key rotation is coordinated with process restart and old/new root overlap; it is not silent hot reload.
Detailed runtime configuration and lifecycle contracts: API management issuer. Protected guest key/token delivery and authenticated enrollment transport are still required before a reachable machine listener or runner is enabled.