Publish is the moment a mutable sandbox becomes a frozen, reproducible artifact ready to serve production traffic. The final runtime can be standard MIOSA Deploy or App Engine. The public URL always comes from MIOSA.
The pipeline
- Sandbox /workspace (mutable; agent or developer is editing here)
- Source snapshot (tarball, sha256-keyed, stored in object storage)
- Build (static packager, auto-detect, or explicit commands)
- Release (static artifact or dynamic server artifact, sha256-keyed)
- Deployment Version (database row pointing at the release)
- Health check (for dynamic; static skips this)
- Promotion (Deployment.active_version_id updated)
- Proof (route, runtime, and URL verified)
The sequence below shows who acts at each step:
You make one publish request. MIOSA does all of this internally and returns the result when the version reaches a terminal state. For App Engine, MIOSA also ensures the workspace host, starts the app container, and proves the route before the deployment is treated as live.
The publish call
Response shape:
{
"data": {
"deployment": {
"id": "dep_...",
"active_version_id": "ver_...",
"state": "running",
"public_url": "https://smile-dental.apps.cliniciq.com"
},
"version": {
"id": "ver_...",
"kind": "static",
"state": "ready",
"artifact_sha256": "...",
"source_sha256": "..."
},
"services": [
{ "id": "svc_...", "type": "static_web", "state": "healthy" }
],
"promoted": true
}
} Source snapshots
Before the builder runs, MIOSA freezes a snapshot of the sandbox’s source. The snapshot is a tarball of /workspace (or whatever output_path you specify), excluding cache directories that don’t need to ship:
included: application code, package manifests, lockfiles, public assets, config
excluded: node_modules, .next/cache, dist (will be rebuilt), .git (unless needed),
__pycache__, build/, target/ The snapshot is stored and sha256-keyed. The version record stores source_sha256 so retries can rebuild from the exact same input without re-snapshotting.
The sandbox keeps running while the build runs. You can keep editing.
Build modes
MIOSA picks a build mode from the source. You can override with build_command if needed.
| Mode | Trigger | What happens |
|---|---|---|
| Dockerfile | Dockerfile exists in output_path | MIOSA builds the container-style server artifact |
| Auto-detect | package.json / requirements.txt / pyproject.toml / go.mod / mix.exs | MIOSA picks install/build/start defaults |
| Static packager | Only static files in output (no server entrypoint detected) | Files are stored as a static release |
| Explicit | buildCommand and/or runCommand provided | MIOSA runs those, then packages whatever’s in output_path |
App Engine publish
Use App Engine for workspaces that publish many small apps, funnels, lead magnets, APIs, or client sites. The app runs as a container on the workspace App Engine host, while MIOSA owns routing, TLS, domains, billing attribution, and the Deployment record.
miosa sandbox publish <sandbox-id>
--path /workspace
--slug my-app
--build-command "npm run build"
--run-command "npm run start"
--port 3000
--docker-deploy
--wait
--timeout 900
--json For an update, add --app <deployment-id>.
This targets the existing deployment instead of creating another one.
Then prove the deployment:
miosa deploy prove <deployment-id> --json
miosa docker-deploy doctor <deployment-id> --probe-path / --json The command name and API path still contain docker-deploy because that is the compatibility wire value.
Customer-facing product language is App Engine.
What gets promoted, and when
Static publishes promote as soon as the artifact is uploaded - there’s nothing to health-check.
Dynamic publishes don’t promote until:
- The build succeeded.
- The dynamic release artifact was stored.
- New production runtimes were scheduled.
- Health checks (
GET healthCheckPath) returned 2xx for the configured grace period (default 30s healthy before promotion).
If health checks fail, the previous version stays live and the new version is marked failed. No traffic is sent to the broken release. Your last known good production keeps serving.
Idempotency
Pass Idempotency-Key on every publish. If the same key is sent twice within the retention window, MIOSA returns the original result without re-building. This makes retries safe - useful for shaky network connections and worker queues.
Failure modes
| Failure | What happens | Recovery |
|---|---|---|
| Build command exits non-zero | Version marked failed, log uploaded, production unchanged | Fix in sandbox, publish again |
| Health check never passes | Version marked failed, runtime instances torn down | Fix run command or healthCheckPath, publish again |
| App Engine host is not active | Publish waits or returns not ready | Run miosa docker-deploy ensure --wait --json, then publish again |
| App Engine app row is missing | Proof fails | Re-publish with --docker-deploy --wait, then run doctor again |
| Source snapshot fails | Publish returns 5xx immediately, no version row created | Retry the request |
| Static build exceeds artifact size limit | Version marked failed | Reduce assets or contact support to raise limit |
| Two publishes race for the same deployment | Second one waits on the advisory lock; both eventually succeed in order | None - handled automatically by our operation lock |
See also
- Versions - what publish produces
- Releases - the artifact itself
- Runtime Instances - for dynamic deploys
- App Engine - for workspace app containers
- Rollback - when health checks pass but the app turns out broken