Skip to content

Helm Chart

The chart in apps/backend/kubernetes/helm/ deploys the application and its execution infrastructure. Keep non-secret operator settings in reviewed values files and apply credentials as existing Kubernetes Secrets.

ComponentDefault
API and webOne replica each
Audit workerSeparate Deployment, database role and service account
Rust execution managerOne replica, ten active runs, two additional warm slots
Queue bridgeOne Redis consumer with concurrency ten
Single-use runner and gatewayCreated dynamically for each warm slot
RustFSOne persistent Pod, initializer and two public data gateway replicas
RedisAuthenticated single instance with persistence
Internal CockroachDBOne evaluation node with TLS and password authentication
Database migrationRelease-specific Job
Prometheus, Grafana and TempoEnabled
Compiler, signaling, sink services and public ingressOptional

The executorPool Deployment is rendered only in trusted_shared mode. Its values do not control isolated capacity. Default values require generated credentials and public endpoints before installation; images default to the published dev tag and isolated mode additionally requires digests.

execution:
isolationMode: per_run
backend: http
asyncBackend: redis
queueName: exec:jobs:v3
executionManager:
replicaCount: 1
workerThreads: 2
maxConcurrentExecutions: 10
warmPoolSize: 2
warmPoolCreationConcurrency: 2
warmPoolMaxAgeSeconds: 600
startupGraceSeconds: 30
terminalGraceSeconds: 60
cleanupTimeoutSeconds: 30
executor:
timeout: 3600
runtimeClass:
create: false
name: runsc
handler: runsc
networkPolicy:
enabled: true

Install gVisor and Cilium before applying these values. The chart’s isolated policy resources require the Cilium CRD and deny node, Kubernetes API and metadata egress. It refuses isolated deployment with disabled NetworkPolicy, missing image digests or incompatible dispatch settings.

Manager replicas each maintain their own additional reserve. Set executionManager.sandbox resource limits and node placement to match the execution nodes. See Executor for the relationship between concurrency, reserve, preparation rate and throughput.

All referenced Secrets must exist in the release namespace.

Helm referenceRequired keys
jwt.existingSecretBACKEND_KEY, BACKEND_PUB, BACKEND_KID
api.existingSecretSINK_TOKEN_ENCRYPTION_KEY, SINK_SECRET, MAINTENANCE_TOKEN
execution.existingSecretEXECUTION_MANAGER_TOKEN
rustfs.existingSecretRUSTFS_ROOT_USER, RUSTFS_ROOT_PASSWORD
storage.s3.existingSecretAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, STS_ISSUER_ACCESS_KEY, STS_ISSUER_SECRET_KEY
redis.auth.existingSecretREDIS_PASSWORD and complete URL-encoded REDIS_URL
redis.externalExistingSecretComplete authenticated REDIS_URL
database.external.existingSecretDATABASE_URL
database.apiExistingSecretAPI DATABASE_URL for bundled SQL
database.migration.existingSecretSchema-owner DATABASE_URL
audit.database.existingSecretWorker DATABASE_URL
audit.entrySecretAUDIT_ENTRY_KEY, optionally AUDIT_ENTRY_KEY_PREVIOUS
audit.existingSecretAUDIT_SIGNING_KEY, or the chosen key service’s credentials
audit.bucketSecretAUDIT_BUCKET_ACCESS_KEY_ID, AUDIT_BUCKET_SECRET_ACCESS_KEY

Setup generates these contracts together. BACKEND_KEY and BACKEND_PUB contain base64-encoded ES256 PEM material; Kubernetes Secret encoding is a separate layer. Executors receive the public material only.

External Redis requires redis.enabled=false. External SQL requires database.type=external; set database.external.provider to postgresql or cockroachdb. The default bundled database must remain one node.

The API ingests audit records and verifies public signatures. The separate worker seals records, signs checkpoints and writes archives. Its signing key or transit token, bucket credentials and SQL login never enter the API Pod. The object-store initializer receives only the bucket credentials. New bundled audit buckets use Object Lock in COMPLIANCE mode with four years of retention.

Setup generates a shared entry key and separate API, worker and migration SQL credentials. The migration Job applies the schema, then grants the API read access to audit evidence and permission to insert new records. Only the worker can change existing evidence. For external SQL, provide DATABASE_URL for the schema owner, API_DATABASE_URL and AUDIT_DATABASE_URL with distinct users and passwords, all using sslmode=verify-full. A private CA can be supplied through DATABASE_CA_FILE; include sslrootcert=/etc/database/ca.crt in those URLs.

Bundled CockroachDB requires the generated TLS Secrets. The node key is mounted only by the database, and the root client key only by its initialization Job. API and worker Pods receive the public CA certificate. Node and root client certificates expire after one year. Retain the generated database-ca-admin Secret, which contains the CA signing key and is never mounted by a workload, for operator-led certificate renewal.

An existing installation using --insecure needs a planned change to TLS before this chart can start its database. Preserve the database volume and existing credentials, provision the separate TLS and runtime-role Secrets, and review the rendered migration and database workloads before upgrading. Removing TLS settings does not restore insecure mode.

The worker’s audit policy is the audit section of the flow-like.config.json compiled into its image, from the same FLOW_LIKE_CONFIG build input as the API image (FLOW_LIKE_BUILD_CONFIG in scripts/build-images.sh). It needs no config Secret or mount. api.runtimeConfig overrides the API’s configuration only and never reaches the worker, so a changed audit policy needs a rebuilt worker image. The worker waits for the migration Job of its revision before it starts; that Job owns and verifies the SQL boundary between the API and worker logins. To pause the worker, set audit.replicaCount=0: nothing is sealed or archived while it is scaled down, and grants and credentials are untouched.

audit.worker=false stops the dedicated worker; it does not start one inside the API. The chart rejects sinkServices.enabled while the worker is enabled because the API’s CronJob creation permission could otherwise mount the worker’s Secrets. Isolate that scheduler before combining these features. An external key service on a private address or nonstandard port needs a matching audit.extraEgress rule. See Audit trail for checkpoint retention and independent verification.

Bundled storage uses storage.provider=s3 and rustfs.enabled=true. The public, internal and STS origins, bucket names and session lifetime are values; the root, API and issuer credentials are separate Secrets. For external storage, set rustfs.enabled=false and supply all endpoints and a provider with the required session-policy enforcement.

The isolated chart supports this S3 path. The AWS, Azure, GCP and R2 provider blocks remain available in trusted shared mode. Follow Storage for their boundaries and the R2 temporary-token requirement.

The llm.*.existingSecret values configure the API’s hosted-model proxy. For example, OpenRouter uses OPENROUTER_API_KEY and optionally OPENROUTER_ENDPOINT; OpenAI uses HOSTED_OPENAI_API_KEY and optionally HOSTED_OPENAI_ENDPOINT. These installation-wide keys do not enter isolated runners.

Every first-party image is a map with repository, tag, digest and pullPolicy:

ValuePublished repository
api.imageghcr.io/rheosoph/flow-like-kubernetes-api
audit.imageghcr.io/rheosoph/flow-like-audit-worker
web.imageghcr.io/rheosoph/flow-like-kubernetes-web
executor.image, executorPool.imageghcr.io/rheosoph/flow-like-kubernetes-executor
executionManager.imageghcr.io/rheosoph/flow-like-kubernetes-execution-manager
database.migration.imageghcr.io/rheosoph/flow-like-kubernetes-migration
sinkServices.imageghcr.io/rheosoph/flow-like-kubernetes-sink-trigger
executionManager.queueBridge.imageghcr.io/rheosoph/flow-like-docker-compose-runtime
compiler.imageghcr.io/rheosoph/flow-like-docker-compose-compiler
signaling.imageghcr.io/rheosoph/flow-like-docker-compose-signaling
rustfs.bootstrap.imageghcr.io/rheosoph/flow-like-docker-compose-object-store-init

Defaults use the mutable dev tag with pullPolicy: Always. A non-empty digest must be sha256: followed by 64 hex characters; the chart then renders repository@digest and ignores the tag. scripts/resolve-images.py fills the digests for one published tag and scripts/build-images.sh records them after a push; both write .generated/values-images.yaml, which deploy.sh applies after every operator file.

In isolated mode, executionManager.image.digest pins the manager and enforcement gateway, while executionManager.sandbox.image is a full repository@sha256:... executor reference. Rebuild and push the manager and executor together when changing their protocol. The runner image contains the Rust slot adapter.

global.imageRegistry is a literal repository prefix; include its trailing slash when setting it manually. Generated files set this prefix to an empty string because their repositories are complete. values-production.yaml shows the mirror pattern: prefix registry.example.com/, the published repository names and filled digests. Use global.imagePullSecrets for forks, private mirrors and the private Cloud packages; the API forwards the same names to the execution Jobs it creates (K8S_IMAGE_PULL_SECRETS) and to sink CronJobs (SINK_IMAGE_PULL_SECRETS), and the manager forwards them to sandbox Pods. Those Jobs and CronJobs are created in the release namespace (K8S_NAMESPACE is always set from the Pod’s namespace), where the chart’s RBAC and pull Secrets live.

API and web ingress share the chart’s ingress block:

ingress:
enabled: true
className: nginx
hosts:
- host: api.example.com
paths:
- {path: /, pathType: Prefix, service: api}
- host: app.example.com
paths:
- {path: /, pathType: Prefix, service: web}
tls:
- secretName: flow-like-tls
hosts: [api.example.com, app.example.com]

Object data ingress is configured separately under rustfs.gateway.ingress. Preserve its signed host and path. Keep internal STS, manager, database, Redis and metrics endpoints private.

The chart derives nginx streaming timeouts from execution duration. Other ingress controllers and upstream load balancers need equivalent settings.

From apps/backend/kubernetes/:

Terminal window
./scripts/deploy.sh \
-f helm/values-production.yaml \
-f values-operator.yaml

The helper starts with .generated/values-generated.yaml, appends .generated/values-images.yaml after the arguments, lints and renders the ordered values, checks Cilium and waits for the release workloads and initialization Jobs. Apply generated Secrets separately before the first install. It does not create the namespace or rotate credentials.

Keep Redis replay claims, cancellation records and persistent storage during upgrades. Drain accepted work before changing queue protocols; rebuild both pinned manager and executor images when their wire protocol changes. Back up and review SQL schema changes before replacing API images.

Terminal window
helm status flow-like -n flow-like
helm get values flow-like -n flow-like
helm history flow-like -n flow-like
kubectl get events -n flow-like --sort-by=.lastTimestamp

Follow Installation for the complete initial setup and Monitoring for scrape configuration and current monitoring limitations.