CloudHub 2.0 deployment tools — design notes¶
This note explains how the JAR-based deployment tooling works and why it is shaped the way it is.
The problem it solves¶
CloudHub 2.0 deployments reference an artifact that already lives in Exchange. Before this tooling there was no way to get a locally built JAR into Exchange from the MCP/CLI surface, and redeploying an existing app was unsafe: the update path re-sent a fully defaulted deployment payload, so any field the caller didn't specify was overwritten with a default — quietly downgrading the runtime and moving the app out of its target/space.
Publishing a JAR¶
The working upload path is the Exchange v2 publication API, not the Maven facade (which rejects
plain PUTs with 412):
POST /exchange/api/v2/organizations/{org}/assets/{groupId}/{assetId}/{version}
Header: x-sync-publication: true
Multipart body:
name = <assetId>
classifier = mule-application
files.mule-application.jar = @<jar>
x-sync-publication makes the call return only once the asset is fully published. Uploads use a
longer timeout than normal API calls (JARs are tens of MB). This is implemented in
ExchangeApi.publishAppAsset, backed by HttpClient.postMultipart (native FormData; the default
JSON content-type is cleared so the multipart boundary is set correctly).
Deploying and the safety model¶
- New app → build a full create payload (runtime, target, replicas, vCores, public URL) from
src/safety/deployment.ts:buildCreatePayload. - Existing app → PATCH only
application.refviaCloudHub2Api.updateArtifactRef. Runtime, target/space, replicas, resources, and settings are preserved by the server. This is the invariant that prevents accidental downgrades/relocations, and it is enforced by a regression test. - Settings update → PATCH only
application.configurationwith the merged application-properties service. Existing masked secure-property entries are copied unchanged unless replaced by the caller; artifact and infrastructure fields are never replayed. - Start/stop → PATCH only
application.desiredState.
Both the MCP tools and the CLI deploy command call the same shared builder/merge helpers, so the
two surfaces cannot drift.
Dry-run + confirm¶
MCP runs over stdio, where there is no interactive confirmation prompt. Instead, every mutating deploy
tool is dry-run by default: without confirm: true it returns a preview of exactly what would
change and does nothing. Re-calling with confirm: true applies. deploy_jar's preview publishes
nothing either — publish happens only on confirm.
Application deletion is stricter: delete_app returns the current deployment ID in its preview,
then requires both confirm: true and that exact expectedDeploymentId. Production additionally
requires confirmProduction: true. A recreated deployment with the same name has a different ID and
is never deleted under an earlier confirmation. The CLI provides the equivalent flow through
anc apps delete, --confirm <deployment-id>, and --allow-production.
Tools¶
| Tool | Kind | Purpose |
|---|---|---|
get_deployment_spec |
read | Full current spec — the look-before-you-leap view |
publish_app_jar |
write | Upload a built JAR to Exchange |
update_app_artifact |
write | Safe redeploy — artifact ref only, optional wait |
rollback_app |
write | Revert to a complete historical artifact ref, skipping lifecycle-only specs |
deploy_jar |
write | Publish + create-or-update in one call |
deploy_app |
write | Create new, or safe artifact-only redeploy of an existing app |
delete_app |
write | Delete only a deployment using bound two-step confirmation |
Conventions & open points¶
- Asset ID default: the JAR filename without its
.jarextension. Callers can overrideassetId. assetIdvsartifactId: Exchange calls the coordinateassetId; CloudHub calls itartifactId. They carry the same value here and are mapped explicitly at the deploy boundary.- vCores: written to
application.vCores(the field CloudHub 2.0 surfaces and this tooling reads back in status), validated against the allowed size set.