# Deploy Contract

`deploy.schema.json` defines the current `deploy/deploy.yaml` manifest shape.

The MVP acceptance environment is DEV only. Public endpoint source fields live in
`deploy/deploy.yaml`: frontend `http://74.48.78.17:16666` and API/edge
`http://74.48.78.17:16667`. PROD profile fields may be represented in schema
for future compatibility, but PROD deployment is not part of MVP acceptance.

## L5 DEV Skeleton

- `deploy/deploy.yaml` is the DEV-only deploy manifest.
- `deploy/deploy.yaml` is the single source for `health.path`,
  `publicEndpoints`, `frp.proxies`, and `k3s.serviceMappings`.
- Config parsing and writing must go through `scripts/src/structured-config.mjs`
  and `scripts/src/deploy-config.mjs`; do not add direct YAML parser calls in
  planners, renderers, smoke scripts, or CLIs.
- `deploy/k8s/base` contains parseable k3s resource skeletons for the frozen
  HWLAB service IDs rendered from the manifest contract.
- `deploy/k8s/dev` contains the DEV health contract and endpoint metadata.
- `hwlab-cloud-api` declares the DEV DB env contract with
  `HWLAB_CLOUD_DB_URL` from Secret reference
  `hwlab-cloud-api-dev-db/database-url` and non-secret
  `HWLAB_CLOUD_DB_SSL_MODE=disable`. Runtime DB readiness dials the redacted
  host parsed from that Secret URL. `cloud-api-db` is only an optional desired
  alias until Service plus Endpoint/EndpointSlice ownership and rollout/apply
  contract exist; see
  [docs/reference/dev-runtime-boundary.md](../docs/reference/dev-runtime-boundary.md).
  The repository records names only, never secret values or a live DB
  connection string; runtime health reports redacted env injection and DB
  connection result classifiers.
- `hwlab-cloud-api` declares the DEV Code Agent provider contract with
  `HWLAB_CODE_AGENT_PROVIDER=codex-stdio`, `OPENAI_API_KEY` from Secret reference
  `hwlab-code-agent-provider/openai-api-key`, and
  `HWLAB_CODE_AGENT_OPENAI_BASE_URL` through the DEV egress/proxy path. The same
  contract declares `/workspace/hwlab` as a writable workspace,
  `CODEX_HOME=/codex-home`, `/app/node_modules/.bin/codex` as the Codex command,
  and repo-owned `codex app-server --listen stdio://` supervisor mode. Source checks verify the Secret ref,
  base-url, workspace, CODEX_HOME, config/auth mounts, NO_PROXY, and CLI contracts without printing API key
  material. Controlled apply reports also check that the live
  `hwlab-cloud-api` Deployment preserves the same env names and `secretKeyRef`
  name/key metadata after apply without reading Kubernetes Secret data.
- `hwlab-cloud-api` also declares `HWLAB_CLOUD_RUNTIME_ADAPTER=postgres` and
  `HWLAB_CLOUD_RUNTIME_DURABLE=true` so DEV can use the Postgres-backed runtime
  adapter once the schema is applied. `/health/live` must still remain degraded
  if DB auth, DB connectivity, or runtime schema readiness is blocked.
- `deploy/k8s/prod` is a disabled placeholder gate only.
- `deploy/frp` describes the D601-to-master reverse link without secrets.
- `deploy/master-edge` describes public edge ownership and health boundaries.

Dry-run source rendering:

```sh
node scripts/deploy-contract-plan.mjs --pretty
node scripts/deploy-contract-plan.mjs --check
```

The deploy contract plan renders and validates only. It does not run frps/frpc,
does not call `kubectl apply`, does not restart services, and does not touch
PROD. Later work can wire the same source fields into `hwlab edge apply` or
`deploy apply`.

Desired-state commit/image convergence review:

```sh
node scripts/deploy-desired-state-plan.mjs --plan --pretty
node scripts/deploy-desired-state-plan.mjs --check
node scripts/deploy-desired-state-plan.mjs --target-ref origin/G14 --pretty
node scripts/deploy-desired-state-plan.mjs --target-tag <tag> --pretty
node scripts/deploy-desired-state-plan.mjs --promotion-commit <sha> --check
```

The desired-state plan reads `deploy/deploy.yaml`,
`deploy/artifact-catalog.dev.json`, `deploy/k8s/base/workloads.yaml`, and the
optional artifact report snapshot. `deploy/deploy.yaml` is the human-authored
runtime config truth source. Published image identity lives in
`G14-gitops:deploy/artifact-catalog.dev.json`; the source branch catalog is only
a seed contract for local checks. GitOps render overlays catalog image fields and
generated env mirrors such as `HWLAB_COMMIT_ID`, `HWLAB_IMAGE`,
`HWLAB_IMAGE_TAG`, and `HWLAB_SKILLS_COMMIT_ID` into runtime manifests.

The plan blocks if generated artifact identity leaks back into `deploy.yaml`,
or if `hwlab-cloud-api` loses the Code Agent provider Secret ref
`hwlab-code-agent-provider/openai-api-key`, the `OPENAI_API_KEY` env name, or
the DEV egress proxy base-url env. Promotion updates must refresh only the
workspace catalog from a publish report before render; Tekton writes that
catalog to `G14-gitops`, not back to the source branch. Human operators should
not run catalog refresh as a source update. Report files are contextual evidence
only and must not override human-authored source truth.

This is source/dry-run support only. It does not prove a registry image exists,
does not build, pull, push, apply, restart, or touch PROD, and must not be used
as M3 DEV-LIVE evidence.

## D601 Native k3s Guard

DEV deploy and smoke commands must target D601 native k3s with
`KUBECONFIG=/etc/rancher/k3s/k3s.yaml`. Do not use the default kubeconfig if it
points at `docker-desktop`, `desktop-control-plane`, or `127.0.0.1:11700`.
Control-plane confusion is tracked in
[pikasTech/unidesk#138](https://github.com/pikasTech/unidesk/issues/138).

Next DEV deploy smoke commands, for a separately authorized deployment task:

```sh
npm run check
node --input-type=module -e "import { readDeployConfig } from './scripts/src/deploy-config.mjs'; await readDeployConfig(process.cwd())"
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl apply --dry-run=server -k deploy/k8s/dev
curl -fsS http://74.48.78.17:16667/health/live
node scripts/dev-edge-health-smoke.mjs --live --report /tmp/hwlab-dev-gate/report.json
```

Do not run PROD deployment or substitute UniDesk backend, provider-gateway, or
microservice proxy for HWLAB runtime services.
