Deployment and runtime architecture
The question from the review: what exactly gets deployed? Is the deployable unit a project, a workflow, a container, a Node.js server, a REST service? And can Allvue build and run it themselves, without depending on Mindshine infrastructure afterwards?
The short answer
The deployable unit is a container image per app service, built from a Dockerfile and a source tree that belong to the project and are exported verbatim. A project is delivered as ordinary source: one directory per app service, a compose file, optional Kubernetes and Azure Container Apps definitions, a generated CI pipeline, a runbook and the tests. Allvue builds the images in their own CI, runs them in their own VPC, and nothing in a delivered application authenticates against cortex, phones home or checks a licence. Available
Workflows, agents and pipelines are not deployable units. They are data executed by the engine runtime, and that runtime ships only when the delivery distributes a feature that needs it. By default a customer receives exactly the application.
The chain
| Stage | What it is | Standing |
|---|---|---|
| Cortex project | Configuration rows in the project's database (sources, rules, requirements, declarations) plus the engine fork (endpoints, services, app services). While cortex hosts the project, every app service also runs on the platform's Kubernetes cluster at its own hostname. | Available |
| Generated source | The export writes every app service under app-services/<name>/ with its Dockerfile and source tree byte for byte, a service.json (port, health path, route, dependencies, environment keys) and an .env.example. Secrets are blank in every committed file. |
Available |
| Git repository | Every export is a commit. When git hosting is configured for the installation, every project gets a private repository at creation and every export is pushed to it; the same export can be pushed to a repository Allvue designates. | Available |
| CI | The delivery options generate either a GitHub Actions workflow (a matrix over every Dockerfile, tagging images with the commit SHA, deploy jobs gated on repository variables) or an Azure Pipelines definition (a build-and-push matrix through a Docker Registry service connection, deploy stages gated on a service connection). Allvue's existing CI can build the same Dockerfiles with no change. | Available |
| Images | One image per app service, from its own Dockerfile. No cortex layer, no agent, no injected code. | Available |
| Deployment targets | Docker Compose (always), Kubernetes manifests (deployment with probes, persistent volume, cluster service, ingress for public HTTP services, an example secret, a kustomization), Azure Container Apps (Bicep with an external or internal ingress per service, secure parameters, probes, a deploy script). | Available |
| Integration with Allvue systems | Through the application's own HTTP surface and its own connection strings: an ordinary service in the VPC. | Available |
The nine questions, answered directly
What is in the container?
Exactly what the app service's Dockerfile builds. There are two shapes today:
- A fleet-built browser application. The scaffold is a React module compiled by webpack into a static bundle, served by a small Node server on the
node:20-alpineimage. The server does three things: serves the bundle, proxies/api/*to the platform API for sign-in and data calls, and answers/health. It has no framework dependency. The whole server is about 150 lines and ships in the tree. - Any other runtime. The first production app service is a .NET 8 identity service (Duende IdentityServer) with a SQL Server development stand-in and a sample relying party. Java, Go and static-site services take the same contract: a port, a health path (or a plain TCP check), optional environment values.
The service manager does not know or care what is inside the image. It writes the tree to disk, runs the image build and starts the container with the project's environment only. Platform configuration and platform secrets are never mounted. Available
One container per project, service or workflow?
One container per app service. A project typically has one to three: the application, sometimes its own database as a development stand-in, sometimes a second service such as an identity provider. A workflow is never a container: it is rows in the project's relational database executed by the engine runtime. Available
The engine runtime itself is a set of containers that ships only when a distributed companion needs it: the endpoints runtime, the engine catalogue store (MongoDB), the cache (Redis), the versioned configuration store (Dolt), a relational store (PostgreSQL) with a seed step, and any function services the project owns. The operations pages (a chromeless portal and its API) and the agent chat are separate containers, again only when their companion is distributed.
What runtime libraries are included?
Whatever the app service's own dependency file names (package.json, the .NET project file). The platform adds nothing to the image. The one optional touchpoint is the cortex app SDK, a single script tag an application includes if its owner wants the cortex side menu inside it; without the tag the application has no reference to cortex. Available
How do workflows invoke tools?
A workflow step is assigned to exactly one owner: a role, a person, an agent actor, or an endpoint step. An endpoint step calls a fork endpoint directly when the run reaches it. An agent actor step files a task; a worker claims it, runs the named agent with the step context, and the agent's tools are the endpoints its manifest names. The engine stamps the identity in whose name a step completes server-side; a browser never chooses it. Off the platform, workflows run when the Workflows companion is distributed, and agent actor steps additionally need the agent runtime and a model credential the customer supplies. Available on the platform; Partial in a delivery (agent actors need a model credential).
How are agents represented?
As a manifest: an endpoint on the fork whose payload names the agent's display name, persona, model, tools (each a typed input contract plus the fork endpoint it calls) and safeguards. There is no agent code. At run time the assistant starts one headless coding-agent process (Claude Code) per turn or task in a fresh temporary directory with two files: the persona and an MCP configuration naming one server, the platform's internal agent endpoint, with an opaque handle that resolves server-side to the fork and the manifest. The process has no shell, file or web access. In an export the manifest is source like every other endpoint. Available
How are APIs exposed?
Three surfaces, each ordinary:
| Surface | Exposure |
|---|---|
| An app service's own HTTP API | Its own port, published at its own hostname behind the cluster's ingress (on the platform) or however Allvue publishes services in the VPC. |
| Project endpoints | Reached through one data call on the platform API that names the endpoint and an input, with the caller's verified identity merged in. In a delivery this call is served by the bundle's own operations API when the operations layer ships; an application with its own API does not use it. |
| The platform's own API and MCP | A REST API with an interactive reference per installation, and MCP endpoints per project behind OAuth 2.1. These are platform surfaces and are not part of a delivered application. |
Available
Is cortex required after deployment?
No. With no companion distributed, the customer archive is the app services, the compose file, the environment files, the runbook, the tests service and the project's documentation pages. Nothing in it calls the platform. With a companion distributed, the archive carries the bundle's own copy of what that companion needs, still with no dependency on the platform. Available
Which components can run independently?
| Component | Runs on its own | Needs |
|---|---|---|
| An app service | Yes | Its own database or the systems its connection strings name |
| The tests service | Yes | The targets its specs name; a test whose fixture is absent is skipped with the reason, not failed |
| Workflows | With the engine runtime containers | The project's relational database, which holds definitions and runs |
| Agents in chat or on workflow steps | With the agent chat container | A model credential the customer holds |
| The operations pages (pipeline, governance, models, docs) | With the engine runtime and the portal containers | The versioned configuration store and the relational store in the bundle |
How does Allvue build and deploy the generated code?
From the repository, with the generated pipeline or their own: build every Dockerfile, push to their registry, then docker compose up -d, kubectl apply -k infra/kubernetes, or the Container Apps deploy script. The runbook is generated from the same service definitions the platform ran (ports, health paths, environment keys per service with descriptions, start order, rollback steps), so it matches the version delivered. Available
What an export looks like
A git export of a project with one app service and the Kubernetes target enabled:
app-services/
allvue-dq/
Dockerfile
service.json port, health path, route, dependencies, environment keys
.env.example keys with descriptions; secrets blank
src/ … the application, verbatim
docker-compose.yml one entry per app service, loopback host ports, acyclic start order
infra/kubernetes/ <service>.yaml (deployment, service, ingress, volume), <service>-env.example.yaml, kustomization.yaml, README.md
.github/workflows/build-and-deploy.yml or azure-pipelines.yml
tests/ the requirement-linked tests and the dependency-free runner
docs/RUNBOOK.md generated: components, setup, deployment, health, support, rollback
README.md every directory mapped to an action; no secret is committed
The compose entry the exporter writes for that service:
services:
allvue-dq:
build: ./app-services/allvue-dq
env_file:
- ./app-services/allvue-dq/.env
ports:
- "127.0.0.1:8081:8080"
depends_on:
- allvue-dq-db
restart: unless-stopped
Host ports bind to loopback only; a start-order dependency is declared only where the service tree names the other service, and the graph is kept acyclic (mutual HTTP references are runtime facts, not start-order edges).
Two hosting modes, one artefact
| Mode | Where it runs | Who operates |
|---|---|---|
| Cortex-hosted | The platform's Kubernetes cluster. Each app service is a deployment plus a cluster service plus an ingress in a shared load-balancer group, built in-cluster, published at https://<service>-<project>.apps.<environment domain> or at a customer's own hostname with the customer's certificate. |
Mindshine, while the project is being built and changed |
| Customer-hosted | Allvue's VPC, from the export. | Allvue, from the runbook |
The images are the same. Moving from the first mode to the second is an export, not a rewrite.
Not in place, and what we propose
| Item | Standing | Proposal |
|---|---|---|
| Pushing the export to a repository Allvue owns | Available The exporter pushes to any git remote with a credential; today the remote is configured per project by us. | Allvue names the repository and a deploy credential; we set it on the project and every export lands there. |
| Building the delivered images inside the platform's own deployment namespace | Not built App services run on the platform through the service manager; the separate "deploy to the cluster" path rebuilds endpoints and screens but not app services. | Not needed for Allvue's model (Allvue builds); listed for completeness. |
| Events as an integration channel | Partial Workflows have listeners (mail, upload form, schedule, data condition); a message-queue listener is not built. | Add a queue listener kind when a requirement asks for it; the listener model is data, so it is one engine addition. |