Sealos Deploy (Brain Managed Mode)
Executor half of a two-party contract: this agent analyzes, builds, deploys,
verifies, and repairs inside the sandbox; Brain (the control plane) renders
input forms and gates completion through exactly two MCP tools. Deploy
mechanics (paths, template YAML, databases, verification) come from the
sibling use-sealos skill — resolve ../use-sealos/ against this skill's
directory. This file owns only the managed-mode differences.
Mode gate
Run this flow only when SEALAI_DEPLOY_MODE is exactly managed. Otherwise
follow ../use-sealos/SKILL.md (interactive local
path) and ignore the rest of this file.
In managed mode, before any other work, confirm the MCP tools
template_ready and deployment_completed are available. If either is
missing, stop with a fatal error — never substitute a file, webhook, or text
answer for a missing control tool.
Environment
Brain injects (read these; never enumerate the whole environment):
| Variable | Meaning |
|---|---|
SEALAI_DEPLOY_WORKSPACE |
project root, /home/devbox/project |
SEALAI_NAMESPACE |
target namespace (also SEALAI_DEPLOY_NAMESPACE) |
KUBECONFIG |
injected namespace-scoped kubeconfig — use as-is |
SEALAI_INPUTS_PATH |
fixed user-input file, exists only after the user submits the form |
SEALAI_DEPLOY_LABELS_JSON |
platform ownership labels — forwarded verbatim, see Deploy |
SEALAI_TURN_DEADLINE_AT |
the only hard time limit |
GITHUB_TOKEN |
source builds and private-image pull secrets |
Hard rules (they override the use-sealos execution rules where they differ):
- Never run
sealos-api.py login, OAuth, orswitch. The injected kubeconfig is the only credential;sealos-api.pyandwait-app.shpick it up fromKUBECONFIGautomatically. - Fully non-interactive. Never ask the user anything — values only the user
knows are declared as template
inputsand collected by Brain's form. Mutations inside the namespace, including a delete strictly required for convergence, are pre-authorized; do not pause for confirmation. - Never print or log
SEALAI_DEPLOY_MCP_TOKEN, kubeconfig contents, or any value fromSEALAI_INPUTS_PATH. - No file-based RPC: never create
control.json,inputs-required.json,turn-report.json,verify-report.json, or anything under.sealos/brain/. - Do not end the turn after deploying until Brain has returned
accepted_stop(ortemplate_readyreturnedawaiting_user). A turn that ends without a control call is a contract violation.
Pipeline
1. Classify
Classify the workspace with the use-sealos decision tree (../use-sealos/references/deploy.md, recipes.md): known self-hosted product → store template or official image; user source code → Kaniko build. Skip the preflight/login part of that skill entirely.
2. Build (source path only)
No Docker daemon exists here. Build with the sibling executor:
python3 ../k8s-kaniko-job/scripts/kaniko-build.py \
--image "ghcr.io/<token-login>/<repo>:deploy-<git-short-sha>"
(paths relative to this skill; run it from $SEALAI_DEPLOY_WORKSPACE).
Read ../k8s-kaniko-job/SKILL.md first.
Use the returned digest-pinned image_ref in the template. If pull is
private, create the <app>-pull secret now (command in that SKILL.md) and
reference it from the workload with the fixed literal name.
3. Template at the fixed path
The canonical artifact is exactly
$SEALAI_DEPLOY_WORKSPACE/.sealos/template/index.yaml. Brain reads this
path byte-for-byte — no other location counts.
Store hit: materialize the store template locally, then continue on the raw-deploy path (the store-instance endpoint is forbidden in managed mode — it cannot carry the ownership labels and leaves nothing to hash):
python3 ../use-sealos/scripts/sealos-api.py store-export <template> \ --out "$SEALAI_DEPLOY_WORKSPACE/.sealos/template/index.yaml"Official image / built image: write the template per ../use-sealos/references/platform.md (and databases.md for KubeBlocks blocks).
Managed-mode template requirements on top of the platform contract:
- The file must start with the
apiVersion: app.sealos.io/v1/kind: Templateheader, have a non-emptymetadata.name, and contain resource documents after the first---— Brain rejects the handshake otherwise. - Declare every value only the end user can supply (external API keys, admin
email, ...) in
spec.inputswithrequired: trueand no default. Brain renders its form from exactly these. Everything else belongs inspec.defaults(${{ random(8) }}suffixes stay — the Template API evaluates them at deploy time, not Brain). - Never add labels beyond the platform contract; the ownership labels travel through the deploy call, not the YAML.
4. Handshake: template_ready
sha256sum "$SEALAI_DEPLOY_WORKSPACE/.sealos/template/index.yaml"
Call template_ready with only {"sha256": "<lowercase hex>"} — the hash of
the final file bytes. Edit the file after hashing and the handshake fails
with template_digest_mismatch; rehash and call again.
awaiting_user→ stop the turn immediately. No Template API call, nokubectl apply, nothing. Brain collects the form and resumes this same thread with values written toSEALAI_INPUTS_PATH. After resuming, do not changespec.inputs(Brain rejects the new schema); reruntemplate_readywith the unchanged file, then continue.continue→ deploy the same file.- Tool error → diagnose, fix, retry the same call. Control errors are recoverable; missing tools are fatal.
5. Deploy
cd "$SEALAI_DEPLOY_WORKSPACE"
python3 <this-skill>/../use-sealos/scripts/sealos-api.py deploy \
.sealos/template/index.yaml \
$(test -f "$SEALAI_INPUTS_PATH" && echo --args-file "$SEALAI_INPUTS_PATH")
- User values flow only through
--args-file "$SEALAI_INPUTS_PATH"— never into prompt text, logs, or tool arguments. - The script forwards
SEALAI_DEPLOY_LABELS_JSONto the Template API asextraLabelsautomatically. Never edit, extend, or re-derive those labels, and never inventdeployment-name/template-namelabels. The Instance name comes from the deploy response (response.name) — Brain does not supply one. - Do not call
sealos-api.py adopt(and do not POSTadopt-template-instance). Managed deploys already stampbrain.io/*via extraLabels; a second claim returns 409. The script skips adoption whenSEALAI_DEPLOY_TASK_IDorSEALAI_PROJECT_IDis set, and when the region is not*.sealos.io. - Quota or validation errors: fix the template (re-run step 4 — the hash changed) or report the failure via the normal repair loop. Never shrink resources silently.
6. Verify, then deployment_completed
Verify for real before reporting (../use-sealos/references/deploy.md §Verify; triage failures with operate.md):
HOST=$(kubectl get ingress -l "cloud.sealos.io/deploy-on-sealos=<instance>" \
-o jsonpath='{.items[0].spec.rules[0].host}')
bash ../use-sealos/scripts/wait-app.sh -t 600 ${HOST:+-u "https://$HOST"} \
-l "cloud.sealos.io/deploy-on-sealos=<instance>"
Only after your own checks pass, collect the real references:
kubectl get deployments,statefulsets -l "cloud.sealos.io/deploy-on-sealos=<instance>" \
-o jsonpath='{range .items[*]}{.apiVersion}{" "}{.kind}{" "}{.metadata.name}{"\n"}{end}'
Call deployment_completed with:
workloads: 1–32 refs, each exactly{apiVersion, kind, name, namespace: $SEALAI_NAMESPACE}— no extra fields (the schema is strict). At least one reported workload must be a ready Deployment, StatefulSet, DaemonSet, Job, or Pod; reporting only Instance/App/Cluster objects fails verification. Include KubeBlocks Clusters as additional refs when the app has databases.publicUrl(optional): thehttps://<host>the app serves, only when an Ingress exists and your own probe returned 2xx. Brain re-probes it from outside and requires the tenant domain; if Brain's findings say the URL is outside the tenant domain or unreachable while the workloads are healthy, call again withoutpublicUrl.
Responses and errors:
accepted_stop→ done; end the turn with a normal summary.repair→ the findings are evidence, not commands. Diagnose, fix in place (kubectlpatch/apply/rollout on the existing resources; rebuild the image via step 2 if needed), re-verify, calldeployment_completedagain. Never create a second Instance, never rerun the Template API to "start fresh", never re-evaluaterandom()identity defaults, never ask for new input values.deployment_completed_throttled→ wait ≥5s, call again.deployment_completed_before_template_ready→ run step 4 first.
There is no repair-count limit; the only limit is SEALAI_TURN_DEADLINE_AT.
Routing
| Need | Reference |
|---|---|
| Deploy-path classification, store/instance mechanics, verification, first aid | ../use-sealos/references/deploy.md |
| Template YAML contract (labels, Ingress, resources ladder, storage) | ../use-sealos/references/platform.md |
| KubeBlocks database blocks and credentials | ../use-sealos/references/databases.md |
| Dockerfile authoring for the source path | ../use-sealos/references/build.md §1 only |
| In-cluster image build | ../k8s-kaniko-job/SKILL.md |
| Debugging failed workloads | ../use-sealos/references/operate.md |
Load only what the step needs. The use-sealos sections about login, user confirmation, registry choice, and docker buildx do not apply in managed mode.