Independent applications
An application that cortex builds for a customer is an ordinary application: a source tree and a Dockerfile in the language and framework the customer chooses, running as its own container with its own HTTP surface and its own database. cortex builds it, hosts it, tests it and ships it, but nothing in the application requires cortex at run time unless the customer asks for a cortex feature to be included. This page shows where that independence comes from in the code, which runtimes have been proven, what the customer receives, and what "supportable without cortex" means in practice.
An app service is an arbitrary Dockerfile plus a source tree
The unit cortex builds and runs for a customer is called an app service. The engine defines it in engine/server/src/js/modules/rpc/serviceManager/appServiceCompose.ts, lines 1 to 14:
An APP SERVICE is the other shape: a whole application with its own HTTP surface, a .NET 8 web app, a Go binary, an nginx, whose source tree + Dockerfile come VERBATIM from the project (
appServicescollection:{ name, route, files{path:content}, dockerfile, port, hostPort, environment, healthPath, public?, subdomain?, customDomain? }). This is how a fork hosts a runtime Flow itself does not have (first customer: the eSpace identity service on Duende IdentityServer, 2026-09-05).
Two properties follow from that document shape.
- The build input is the customer's code, unchanged.
filesis a map of path to content (text, or bytes as base64) anddockerfileis the Dockerfile text. The service manager writes them to disk and builds the image with kaniko on Kubernetes or with Docker on a compose stand. It does not generate a server, inject a framework or wrap the application. A function service (the other service shape,indexCompose.ts) is where the manager generates a Node.js or Python server around handlers; an app service is deliberately the shape where it generates nothing. - The runtime contract is HTTP and a health path. The manager needs
port,healthPath(an HTTP path, ortcpfor a non-HTTP service) and, optionally, environment variables. Anything that serves HTTP or TCP on a port satisfies it.
Since 2026-09-08 this is the default shape of every new application the fleet builds. The older shape (a view rendered inside the cortex UI from a Flow endpoint) is maintained for existing apps and migrated when touched.
Runtimes proven so far
| Runtime | Evidence | Notes |
|---|---|---|
| .NET 8 | tests/e2e/flow-app-service-dotnet.mjs: creates a .NET 8 web app as an app service through the fork MCP (create_app_service), builds it, checks status, calls it over HTTP, reads logs, deletes it, and verifies the container and image are gone |
Round trip on the local stand and the basis of the eSpace work |
| .NET 8 with Duende IdentityServer and SQL Server | The eSpace identity service (espace_identity, first customer of the mechanism, 2026-09-05): an OpenID Connect token service, a SQL Server database as a development stand-in, and a sample relying party, all app services on one fork; exported as a customer archive and started from the archive alone |
Described in docs/deployments-delivery.md |
| Node 20 | The React app scaffold in .claude/skills/cortex-app-service/ : node:20-alpine build and runtime, port 8080, a node:http server that serves dist/ and proxies /api/*, webpack plus Tailwind CSS 4 |
The shape the fleet uses for React front ends |
| Python 3.11 | python:3.11-slim services built and run by the same service manager (the record pipeline, the ML service, the RAG service on the shared core) |
Generated as function services; a Python web application is built the same way as any other app service |
| Go binary, nginx | Named in the engine's own definition of an app service as intended runtimes | No customer service yet |
The engine did not need a change for .NET 8. That is the point: adding a runtime is adding a Dockerfile.
How a generated application talks to the world
| Channel | What it is | Dependency on cortex |
|---|---|---|
| Its own HTTP surface | The application listens on its port. On the cluster it gets one Ingress at https://<name>-<projectId>.apps.<env-domain>, or subdomain under the apps domain, or customDomain (the customer's hostname with its own ACM certificate), or public: false for an internal service |
None. Hostname and TLS are deployment configuration |
| Its own database | An app service connects to whatever its environment names. eSpace uses SQL Server. A cortex-governed Postgres target is available if the project has one, and a service reaches it by ordinary connection string | None at run time |
| Other services of the same project | By cluster DNS (app-<projectId>-<name>); on export the names are rewritten to the exported service names so the connection strings keep working |
None |
| The cortex app SDK (optional) | shared/app-sdk/cortex-app-sdk.js, one script tag. Adds a side menu with the companions the owner switched on (Chat, Workflows, Guides, Models, Pipeline, Governance, Docs) and a sign-in gate. Included only when the customer wants those companions |
Optional. Without the tag the application has no reference to cortex |
| Data endpoints (optional) | A React app built by the fleet usually reads data through project endpoints (POST /api/flow) proxied by its own server. A customer application with its own API does not use this |
Optional |
What the customer archive contains
The customer archive is the delivery a customer deploys themselves (Deployments, Download for local run, "Customer archive"). Its content is governed by exportConfig.delivery (engine/server/src/js/modules/rpc/exporter/delivery.ts, shape by packageLocal.ts, documented in docs/deployments-delivery.md).
| Content | Setting | Default |
|---|---|---|
app-services/<name>/: Dockerfile and source tree verbatim (binary files as bytes), service.json (port, health, route, dependencies, env keys), .env.example and the filled .env |
appServices |
on |
docker-compose.yml with one entry per app service, loopback host ports, depends_on derived from references between services (acyclic by construction) |
targets.compose |
always on |
infra/kubernetes/: Deployment with readiness and liveness probes, PVC at /data, ClusterIP Service, Ingress for public HTTP services, Secret example, kustomization.yaml |
targets.kubernetes |
off |
infra/azure-container-apps/: main.bicep, parameters.example.json, deploy.sh |
targets.azureContainerApps |
off |
CI: .github/workflows/build-and-deploy.yml or azure-pipelines.yml (build matrix over every Dockerfile, deploy stages gated on repository variables) |
ci |
none |
docs/RUNBOOK.md and root README.md, generated from the same service definitions the manager runs: components, environment tables with descriptions, deployment per target, health, support, rollback, project documentation index |
runbook |
on |
Development stand-ins (a sample database) in docker-compose.dev.yml only; the production compose, manifests and CI never carry them |
devServices |
none |
| cortex companions: chat, workflows, guides, models, pipeline, governance, docs | distribute.* |
all off |
Two rules decide the archive's shape (packageLocal.shapeBundle):
- When no companion is distributed, no cortex runtime ships. The archive is the app services, the compose file, the
.envfiles, the runbook, the tests service and.vault/(the project's own documentation pages). No engine, no portal, no agent runtime.BACKEND_URLis written blank so an application whose optional operations layer keys off it stays self-contained. - When a companion is distributed, only what it needs ships. Workflows, Models, Pipeline, Governance or Docs bring the endpoints runtime and the chromeless operations pages; Guides brings the pages only; Chat brings the agent chat. Endpoints are pruned to match.
Every credential in an archive is minted for that archive: the operator login, JWT secrets, the store passwords, each database role's password. Platform passwords are scrubbed from the data dump. Only graph nodes the exported endpoints reach are flattened, so shared platform configuration cannot ride along. Each archive is grepped for key shapes (sk-ant-, AKIA, JWTs, private keys, Password=) and for the vendor's names before handover.
"Supportable without cortex", concretely
| Claim | What it means in the archive |
|---|---|
| Ordinary source code | app-services/<name>/ is the application as written. It can be committed to the customer's repository as-is. A fork's source is also pushed to a private Bitbucket repository on every export, so the git history exists from day one |
| Ordinary containers | Each service is one Dockerfile. docker compose up -d --build starts the delivery on a laptop; the Kubernetes manifests and the Bicep template deploy the same images to the customer's cluster or Azure subscription |
| Ordinary CI | The generated GitHub Actions or Azure Pipelines definition builds and pushes every Dockerfile and deploys when the target's variables exist. It contains no cortex step |
| Tests that run anywhere | Requirement-linked tests are declarative JSON (cortex-tests/1; kinds endpoint, http, sql, browser, agent; the agent kind needs the cortex assistant and is skipped, with the reason, where it is absent). One runner, shared/test-engine/runner.mjs, has no dependencies (pg only for sql tests) and runs as node runner.mjs. The archive ships it as a tests compose service with ./test.sh. A spec names a target, never a host, so the same suite runs in cortex, in the archive and on any machine |
| No license, no callback | Nothing in a delivered application authenticates against cortex, phones home or checks a license. When no companion is distributed there is no cortex process in the archive at all |
| Documentation from definitions | The runbook's environment tables, health URLs, start order and rollback steps are derived from the service definitions, so they are correct for the version delivered |
What a customer keeps if they leave
Everything in the archive: the application source and Dockerfiles, the compose file and infrastructure templates, the CI definition, the runbook and README, the project's Knowledge pages in .vault/, the test suite and its runner, and the application's data in its own database. The private git repository of the fork holds the history. Nothing needs to be rewritten to keep running.
What a customer gains if they stay
| Capability | What it does |
|---|---|
| Requirements chain | Requirement sources (documents, meeting transcripts, change requests) are turned into requirement rows with verbatim citations; every passage of a source is covered or explicitly marked as not a requirement. Each row carries status, the implementing service, tasks and evidence. Versions are pinned to the sources they came from and can be diffed |
| The fleet | Change requests are executed by agent sessions that build, change and test the application as app services, filing evidence on the requirement. A build preview with screen mockups is approved before the first build |
| Tests watchdog | Every project's suite runs every 15 minutes, after boot and after each deploy. A transition (green to red, a different failing set, a runner crash) files an alert and posts to Slack. Browser tests drive the application in headless Chromium |
| Delivery on demand | Any version can be exported as a customer archive with the infrastructure targets and CI the customer selects |
| Governance and data pipeline | Versioned data-source configuration in Dolt, processing rules, target databases and workflows with human gates, all available to the application through guarded endpoints |
The stack cortex chooses vs the stack you choose
| cortex chooses (the platform) | You choose (your application) |
|---|---|
| Node.js 24 services, Fastify, a React 19 SPA | Any language and framework with a Dockerfile: .NET 8, Java, Go, Python, Node, static nginx |
| Dolt, Postgres 16, MongoDB 7, Redis 7, S3 for the platform's own state | Your database: SQL Server, Postgres, anything the container can reach |
| AWS EKS with kaniko builds while it is hosted by cortex | Docker Compose, Kubernetes, Azure Container Apps, or your own platform, from the same images |
| Bitbucket Pipelines for the platform's release | GitHub Actions or Azure Pipelines generated for your repository, or your existing CI |
| Anthropic models through Bedrock, a LiteLLM gateway or a vendor API; Claude Code and Codex CLIs on the fleet | No model dependency in the delivered application unless you distribute the Chat companion |
| OAuth 2.1 on its MCP surface, JWT for its own UI | Your identity provider (eSpace runs Duende IdentityServer as one of its own services) |
| A declarative test format and a dependency-free runner | Your own tests alongside; the runner is one file you can keep or drop |