One-click deploy sounds like a button problem. A user clicks once, the platform starts the app, and a working URL appears.
The harder question is what happens behind that button. Did the platform produce a plan first? Will dependencies be created or reused? Which secrets are generated, and which must come from the user? What proves the app is actually ready? If an AI agent starts the same install, does it call the same product path or fall back to hand-written Docker commands?
That is why Appaloft keeps Blueprint separate from MarketplaceListing. A Blueprint is not a marketplace card, and it is not a code scaffold. It is a portable, versioned, instantiable application or service topology definition. The Marketplace is the product surface that helps users discover, inspect, and install those definitions.
This post continues the idea from A deployment control plane for the AI era: if deployments can be started by CLI, Web Console, GitHub workflows, AI skills, and MCP tools, then a one-click deploy should start with a plan, not with execution.
The Button Is Only The Entry
Many deployment products hide complexity behind a button. That is useful for first use, but it becomes fragile if the button is backed by an invisible script.
Teams still need answers:
- what resources the platform plans to create
- whether databases, caches, object storage, and volumes are dependencies or applications
- where connection strings and passwords are represented
- what health check proves the app is usable
- whether an AI agent can complete the task without bypassing the product boundary
Appaloft does not try to make those details disappear. It tries to turn them into structured, reviewable state.
A Blueprint describes application topology: components, runtime strategy, ports, routes, health checks, parameters, secrets, dependency resources, profiles, variants, and upgrade metadata. It should remain the same kind of object whether it appears in a hosted marketplace, an Enterprise private registry, or a local file registry.
Here is a trimmed PocketBase Blueprint example:
schemaVersion: appaloft.blueprint/v1
id: cloud-pocketbase
name: PocketBase
version: 1.0.0
secrets:
- key: POCKETBASE_ADMIN_PASSWORD
source: generated
generation:
bytes: 32
encoding: base64url
resources:
- id: data
kind: volume
components:
- id: pocketbase
kind: service
runtime:
strategy: container-image
image: ghcr.io/muchobien/pocketbase:latest
ports:
- name: http
containerPort: 8090
protocol: http
public: true
healthCheck:
enabled: true
type: http
http:
path: /api/health
expectedStatusCode: 200
usesSecrets:
- POCKETBASE_ADMIN_PASSWORD
usesResources:
- data
There are a few deliberate choices in that small example.
POCKETBASE_ADMIN_PASSWORD is a generated secret. A plan and UI can show that the secret will be generated, which policy it uses, and whether it is required. They should not expose the value. data is a volume dependency, not a separate marketplace app. The HTTP health check points at PocketBase’s own /api/health endpoint instead of treating a running container as enough evidence.
Those details are what make one-click deployment explainable.
Marketplace Is Not The Domain Model
A marketplace listing owns discovery copy. It can have a title, subtitle, category, icon, website link, docs link, screenshots, curation notes, and localized text. In the Appaloft Cloud official catalog, an entry is usually split into two files:
listing.yaml: Cloud marketplace discovery metadatablueprint.yaml: portableappaloft.blueprint/v1manifest
Those files should not collapse into one concept. A listing may say that PocketBase is useful for small product APIs and internal admin tools. The Blueprint decides the install plan. The listing can be sorted, featured, translated, and reviewed; the Blueprint still goes through neutral schema validation, semantic validation, and planning.
This boundary may feel fussy, but it prevents a common mistake: leaking product display concepts into the deployment model.
If featured: true, category labels, publisher review state, billing policy, or team sharing permissions become part of the Blueprint, local Community users are forced to understand hosted Cloud product semantics. If the Marketplace listing directly owns ports, secrets, dependency readiness, and runtime projection, the deployment engine is now coupled to the display layer.
The Appaloft rule is narrower: a MarketplaceListing may point to a BlueprintVersion, but it is not the Blueprint.
Dry Run Before Execution
The first step of a Blueprint install is a dry-run install plan. The planner receives a Blueprint manifest, profile, variant, and user inputs, then emits deterministic operations. Those operations can include create project, create environment, create resource, configure runtime, set variable, bind dependency, and create deployment.
In the Cloud Marketplace v1 path, the dry-run response explicitly carries createsExternalResources: false. That matters. Previewing a plan does not write durable installation state, create provider resources, start a deployment, emit a billing event, or expose secret material in the response.
That gives the UI, API, and AI tools a safe first question: “What would happen if I installed this Blueprint?”
It also leaves room for review. A user can see which components become Appaloft Resources, which dependencies will be bound, which secrets must be provided, which secrets can be generated, and which health checks will prove readiness. Only after that does an accepted install command enter persistence, execution, readback, failure handling, and rollback boundaries.
Preview should not casually become apply. That discipline is useful for both humans and AI agents.
Dependencies Should Not Pretend To Be Apps
Dependency resources are where Blueprint modeling gets easy to blur.
Postgres, Redis, MySQL or MariaDB, ClickHouse, object storage, OpenSearch, and volumes can all be required by an application. They should not become standalone marketplace app cards. They should enter the Blueprint as DependencyResourceRequirement records and appear in the plan as dependency bindings.
For example, an analytics app may need Postgres and ClickHouse. The wrong model is to publish ClickHouse as an app card and ask the user to copy connection strings by hand. A better model is for the Blueprint to declare the ClickHouse requirement, readiness, standard outputs, and component dependencyEnv. During planning, Appaloft carries output fields, secret metadata, readiness, and binding intent. Real passwords, URLs, and provider credentials are resolved through secret refs only after the accepted execution boundary.
Readiness also has to match the protocol the application actually uses. A ClickHouse HTTP client needs HTTP /ping. Postgres needs to accept connections. Redis needs to respond to PING. Container status is not the same fact.
Multi-Component Apps Are Still Apps
Some Blueprints have one service, like PocketBase. Others include several components: a gateway, worker, optional model runtime, background task, and multiple dependency bindings.
Appaloft should not split that kind of application into unrelated Marketplace cards. The user is installing an application topology, not picking random containers. The public BlueprintApplicationBundlePlan groups the components, deployment intents, and dependency-resource bindings from a Blueprint install plan into one application-level plan.
Cloud InstalledApplication is a different boundary. It is not the Blueprint definition and not the Marketplace listing. It is the durable lifecycle created after a user accepts an install: Blueprint version, variant, profile, component resource and deployment refs, dependency bindings, user configuration, readback, failure state, and rollback entrypoints.
That sounds like an aggregate root because it is one. In the aggregate roots post, we described why InstalledApplication owns lifecycle transitions instead of letting a provider adapter or repository mutate status directly.
AI Deployment Needs This Boundary
AI agents are good at turning vague requests into actions: “deploy n8n”, “add Postgres to this app”, “find out why the generated URL is down.” Without structured planning, an agent is tempted to guess commands, read logs directly, SSH into a host, or call provider APIs outside the product.
Appaloft’s goal is for AI to call the same operating path:
- Read the Blueprint or repository deployment config.
- Generate an install or deploy plan.
- Show which resources will be created or bound.
- Identify missing secrets and permission gaps.
- Execute only after user or policy acceptance.
- Observe the result through readback, health checks, and events.
The AI skill tells the agent how to think through the work. The MCP/tool surface gives the agent stable calls into the control plane. The Blueprint plan turns one-click deployment into an intermediate fact that can be inspected.
Those three pieces should not replace one another.
The Cost
This design is less direct than a script.
The Blueprint schema has to be maintained. The official catalog has to be validated. The dependency contract has to cover common resources. Health checks need to match the real app. Runtime smoke tests find annoying details that schema and planning cannot see: an image tag that no longer pulls, a product UI served from /_/ instead of /, a public port that does not speak HTTP, or an app that needs migrations before it is ready.
But those are deployment platform problems. If the schema and dry-run plan cannot explain how an app will start, users and AI agents will not guess correctly during an incident.
Appaloft’s rule for Blueprints is simple: a Blueprint is not a marketplace card and not a Cloud-only product object. It is a reusable deployment topology definition. The Marketplace makes it discoverable. The install plan makes it reviewable. The accepted command moves it into real execution. Readback keeps the system honest after the command returns.
One-click deploy can start with a button. It should not end as just a button.