This controller runs the official Multica daemon on Kubernetes while moving every provider process into an isolated task-<short-task-id>-<generated-suffix> Pod. The Multica backend and task lifecycle remain entirely official; interception happens only at the provider executable boundary.
An installation has one long-lived multica-runtime-controller Deployment and one ordinary shared workspace PersistentVolumeClaim. The Deployment runs the official multica daemon start --foreground process, and both the controller and short-lived task Pods mount the claim.
The image exposes agy, codex, copilot, and pi as provider shims. The official daemon discovers those commands normally. Before the daemon starts, Pi itself installs the declared packages into its normal $HOME/.pi/agent directory on the shared workspace claim. When the daemon starts a task, it supplies MULTICA_TASK_ID, the prepared work directory, arguments, environment, and stdio. The shim then:
- Creates an immutable request Secret with a Kubernetes-generated attempt name, then creates a short-lived Pod that references that exact Secret.
- Mounts the same workspace PVC and operator-provided configuration volumes.
- Executes the real provider binary in that Pod through the Kubernetes exec streaming protocol.
- Proxies stdin, stdout, stderr, cancellation, and the provider exit status back to the official daemon.
- Deletes only that attempt's Pod and request Secret when the provider exits normally.
One logical task may have multiple provider launch attempts. Every attempt receives a distinct generated Secret and Pod name, so residue from a shim process that was killed before deferred cleanup cannot block a retry with AlreadyExists. Cleanup remains best effort after abrupt process termination; the Pod deadline and controller-Pod ownership bound any retained resources.
The official daemon still owns claiming, progress, cancellation, completion, workspace preparation, and every Multica API request. No custom Multica backend image or private task-worker API is required.
Every official CLI request in a task Pod goes to a loopback-only proxy on the normal daemon port. That proxy authenticates to a gateway beside the controller with its exact request Secret name, task identity, and token. The gateway gets that Secret directly, verifies its ownership labels and immutable task request, strips all private authentication headers, and forwards the original HTTP method, path, query, headers, and body to the one official daemon. No endpoint allowlist is applied, and no Multica CLI or backend source is modified.
This intentionally places authenticated task code in the daemon's control-plane trust boundary. A task can call every daemon route, including control routes such as POST /shutdown; the task-scoped token and NetworkPolicy restrict who can reach the proxy, not which daemon operation that task can invoke.
Controller and task Pods use the same immutable image digest. The image contains:
- the official Multica CLI release downloaded from
multica-ai/multicaand verified by SHA-256; - the Kubernetes provider interception shim;
- pinned Codex, Copilot, Antigravity, and Pi CLIs;
- PHP 8.5 with Composer plus the MongoDB, Redis, and Zstandard extensions;
- Node.js 26, Python 3 (
pythonaliasespython3), Go, and Rust (rustc, Cargo, and rustup); - Git/Git LFS, GitHub CLI, k9s, kubectx/kubens, kubectl, AWS CLI, OCI CLI, and Google Cloud CLI;
- zlib and Cyrus SASL runtime/development packages;
- a native build toolchain (
gcc,g++,make, pkg-config, CMake, and Ninja); - LLM-friendly source/data utilities (
jq,yq, ripgrep, fd, patch, rsync, zip/xz, file, and tree); - process/network diagnostics (
procps, lsof, iproute2, dnsutils, and netcat); - ShellCheck/shfmt, uv/uvx, and Corepack.
The image never compiles Multica from a local source checkout.
Pinned runtime, extension, cloud CLI, Multica, and provider versions live in
build/runtime-versions.env. The four-hour automation checks the official
Multica release, updates MULTICA_CLI_VERSION, and increments the root
VERSION patch in the same pull request. A release version therefore reaches
main only with the CLI update it identifies.
# Build and load the host architecture for local verification.
make image \
IMAGE=ghcr.io/korioinc/multica-runtime-controller:dev \
VERSION=devAfter a develop-to-main merge, the Release workflow publishes the
multi-platform image as both the numeric version and latest, then creates the
matching v<VERSION> GitHub Release. The runtime repository owns and completes
only that image and GitHub Release flow. The Helm repository independently
polls the public GHCR package hourly, or performs the same check when maintainers
run its update workflow manually. That repository owns the chart source,
validation, version update, and publishing. Configure both
controller.image.reference and runtime.image.reference with the published
digest.
The repository is public source. No open-source license grant is implied until
a LICENSE file is added by the repository owner. Report vulnerabilities only
through the private process in SECURITY.md, never in public
issues, pull requests, logs, or artifacts.
Three workflows own delivery:
CLI Version Updateruns every four hours or manually. It updates the Multica CLI pin and patchVERSION, opens one PR intodevelop, waits forverifyandruntime-image, squash-merges the exact checked head, and asks the next workflow to create the promotion PR.Create develop to main PRprovides the two required PR checks and keeps one directdevelop-to-mainPR. The promotion remains visible for the maintainer to merge.Releaseruns for everymainpush. It readsVERSION, builds and pusheslinux/amd64pluslinux/arm64to GHCR as<VERSION>andlatest, and creates the matching GitHub Release. It does not invoke or authenticate to the Helm repository.
Bot-created pull requests use an exact-head repository_dispatch check run
because events created by the repository GITHUB_TOKEN do not recursively
start another workflow. GitHub access uses the ephemeral repository-scoped token
and job-specific permissions. The Helm repository discovers newer stable runtime
images from public GHCR on its hourly schedule or through a manual workflow run.
No release gate, repair workflow, or secondary auto-merge workflow is required.
Stable versions match MAJOR.MINOR.PATCH. The CLI updater increments one patch
per actual Multica release change. A release retry accepts only the same main
revision and rejects a tag or GitHub Release owned by another revision.
Scheduled runs are polling;
use the workflow's manual dispatch when a public-repository schedule is paused.
Provider configuration and authentication files must come from Kubernetes Secrets or ConfigMaps, never from the image or Helm values. runtime.extraVolumes and runtime.extraVolumeMounts are mounted into both the official daemon Pod and every intercepted task Pod. Mount each Codex file directly below the normal $HOME/.codex directory. Before a task worker starts, a non-root init container creates only that native directory in the task's ephemeral agent-home; it does not stage or copy configuration.
For Codex:
kubectl --namespace multica create configmap multica-codex-config \
--from-file=config.toml=/Users/jaesung/.codex/config.toml
kubectl --namespace multica create secret generic multica-codex-auth \
--from-file=auth.json=/Users/jaesung/.codex/auth.jsonruntime:
extraVolumes:
- name: codex-home
projected:
defaultMode: 0440
sources:
- configMap:
name: multica-codex-config
items:
- key: config.toml
path: config.toml
- secret:
name: multica-codex-auth
items:
- key: auth.json
path: auth.json
extraVolumeMounts:
- name: codex-home
mountPath: /home/multica/agents/.codex/config.toml
subPath: config.toml
readOnly: true
- name: codex-home
mountPath: /home/multica/agents/.codex/auth.json
subPath: auth.json
readOnly: trueThe controller does not set CODEX_HOME, and the interception layer removes any task-local CODEX_HOME override supplied by Multica. The official daemon and each task Pod therefore use their own standard $HOME/.codex directory directly, with operator files mounted read-only and provider-created state kept in that Pod's ephemeral home volume.
Provider API keys and other environment credentials must come from a Kubernetes
Secret rather than Helm values. Add one or more native EnvFromSource entries
through runtime.extraEnvFrom; the chart applies them to the official daemon,
and the existing provider interception path forwards inherited provider
environment variables to each task Pod.
runtime:
extraEnvFrom:
- secretRef:
name: multica-runtime-provider-envFor GitHub repository access, provide GITHUB_PAT_TOKEN, GITHUB_USER_NAME,
and GITHUB_USER_EMAIL in that Secret. The runtime exports the PAT as
GH_TOKEN, configures gh auth git-credential for GitHub HTTPS credentials,
maps GitHub SSH-style URLs to HTTPS, and supplies the commit author identity
through environment-scoped Git configuration. No SSH agent or writable
known_hosts file is required for GitHub repository operations.
The chart provisions one ordinary shared workspace PVC and uses standard application directories:
$HOME/.codexis each Pod's native, ephemeral Codex home; it is not redirected to the workspace PVC.$HOME/.piis mounted from the PVC's.pidirectory for the controller and every task Pod.$HOME/.multica/pi-sessionsis mounted from the PVC's.multica/pi-sessionsdirectory, which is the session location Multica passes to Pi.
Pi uses these standard paths directly. The controller does not set PI_CODING_AGENT_DIR, and the interception layer removes any task-local override.
Declare reviewed package sources in rollout values. An npm source without a version resolves the latest release whenever the package init container runs:
runtime:
pi:
configMapName: multica-runtime-pi-config
packages:
- npm:pi-mcp-adapter
- npm:pi-thinking-level@0.2.1
- npm:pi-web-access@0.21.0
- npm:pi-openai-service-tier@0.1.4
- npm:@dietrichgebert/ponytail@4.9.0When configMapName is set, every ConfigMap item path is treated as relative to $HOME/.pi and copied there unchanged before package installation. For example, agent/settings.json becomes $HOME/.pi/agent/settings.json; the initializer does not add an agent directory of its own. It ignores auth.json, clears only the packages field in Pi's standard agent/settings.json, and then runs pi install <source> for each declaration. Pi still owns package installation, layout, and settings writes, while runtime.pi.packages remains the package source of truth. Removed package files can remain as an unused cache, but Pi no longer loads them.
Pi packages are executable code. They run with the Pi process's task credentials, filesystem access, and network access. Package changes therefore belong in reviewed rollout configuration, not task commands. Chart validation accepts unversioned npm sources for latest-at-install resolution, exact npm versions, and commit-pinned Git sources. Authentication remains task-scoped and must not be stored in the Pi ConfigMap or PVC state.
The Pi home is persistent, but it is not isolated from task code. Every task mounts the same workspace PVC read-write and can mutate the shared .pi directory. A requirement for task-to-state isolation needs a separate storage and mount boundary.
The official daemon injects task-scoped Multica credentials and agent custom environment variables into the provider shim. Each launch attempt stores the request only in its own immutable, owner-scoped Kubernetes Secret. Normal provider exit deletes that attempt's Secret and Pod; abrupt shim termination may leave them until their existing Kubernetes lifetime bounds remove them.
Create a Secret containing an official Multica user or Cloud Node token. Daemon-only mdt_ tokens are not accepted by the official CLI.
kubectl --namespace multica create secret generic multica-runtime-controller-token \
--from-literal=token='mul_...'Example values:
multica:
baseURL: https://multica.example.com
controllerTokenSecret:
name: multica-runtime-controller-token
key: token
controller:
image:
reference: ghcr.io/korioinc/multica-runtime-controller@sha256:<runtime-image-digest>
pullPolicy: IfNotPresent
runtime:
name: runtime-controller
image:
reference: ghcr.io/korioinc/multica-runtime-controller@sha256:<runtime-image-digest>
pullPolicy: IfNotPresent
capacity: 20
taskDeadline: 6h
pi:
packages:
- npm:pi-mcp-adapter@2.22.0The controller token determines which workspaces the daemon registers. The chart creates and reuses a Kubernetes Secret containing the stable daemon identity, so operators do not need to discover or configure a workspace UUID.
Install the release:
helm repo add korioinc https://korioinc.github.io/helm
helm repo update
helm upgrade --install multica-runtime-controller \
korioinc/multica-runtime-controller \
--namespace multica \
--create-namespace \
-f operator-values.yamlThe chart uses one controller replica because the official daemon owns one stable runtime identity. NetworkPolicies allow only managed task Pods to reach the controller's daemon proxy and otherwise deny inbound traffic to controller and task Pods. Outbound traffic remains unrestricted for Multica, provider CLIs, Git, MCP servers, and web access.
The controller container runs with a TTY because the official Multica CLI writes foreground logs to stderr only when it detects a terminal. This makes daemon and task lifecycle logs available through kubectl logs and K9s instead of only inside ~/.multica/daemon.log.
While a task is running, its Pod also emits provider lifecycle and redacted protocol metadata to stdout. The log includes direction, stream, JSON-RPC method and ID, byte count, and exit code, but never request parameters, responses, prompts, tokens, or other protocol payloads.
kubectl --context local-k3s logs --namespace multica --follow task-<task-id>Pi package installation logs are available from the generated package init containers:
kubectl --namespace multica logs deployment/multica-runtime-controller \
--container pi-package-0The runtime release publishes and verifies only its image and GitHub Release. The Helm repository independently polls public GHCR hourly, and its updater advances the chart version and both digest-pinned image references together when it discovers a newer stable runtime. Roll those chart values and image digests back together on initialization or provider smoke-test failure.
The repository root owns delivery and orchestration assets, while src/ is the
sole Go module boundary:
.
├── build/
├── scripts/
└── src/ # go.mod, go.sum, cmd/, internal/
Run the supported build and verification workflows from the repository root:
make build
make test
make verifyThis runs Go tests, race tests, vet, repository validation, and workflow validation.
For direct Go tool usage, select the nested module explicitly. Root-level
module discovery such as go test ./... is not supported.
go -C src test ./...