--- title: "1.15.9 — Provisioned nodes can finally take a deploy" description: "A provisioned environment came out of `apply` looking finished — nodes serving, HTTPS valid — and could not accept a single deploy. `/opt/api` was not a git checkout, so the deploy plane's `git fetch && git reset` had nothing to work with; no webhook secret was ever generated, so GitHub's push deliv…" date: "2026-08-20" tags: ["release"] canonical: "https://django-mojo.sitesmojo.com/changelog/1-15-9/" --- A provisioned environment came out of `apply` looking finished — nodes serving, HTTPS valid — and could not accept a single deploy. `/opt/api` was not a git checkout, so the deploy plane's `git fetch && git reset` had nothing to work with; no webhook secret was ever generated, so GitHub's push deliveries were rejected as unsigned; and nothing created the webhook. All three are now part of `configure`. Separately, both node health probes dialled plain HTTP against a vhost that 301s everything, so the canary reported every successful deploy as a sanity-check failure and rolled it back, while the post-restart gate passed on the redirect alone with the application dead behind it. The provisioner also now builds the two things an environment needs before it can host a static WebApp: a releases bucket and a KMS key. ### Breaking - `EDGE_SOCKET_BASE` is now published as `/var` instead of leaving the framework default of `/run/mojo`. An existing edge `Upstream` whose `socket_path` sits under `/run/mojo` will fail validation after upgrading. See Upgrade notes. ### Added - `provision apply` creates a releases S3 bucket and publishes it as the sole entry in `EDGE_RELEASE_BUCKETS`. Without one, `releases.register` refuses to mint a WebApp release at all. - `provision apply` creates a KMS key and publishes `KMS_KEY_ID`. `KSMSecrets` models raise without it, so an environment could not hold a dnsman certificate and therefore could not serve an edge vhost. - `provision configure` converts `/opt/api` into a real git checkout against the project's repository, at the commit the node booted from. - `provision apply` mints `github_webhook_secret` and `configure` registers the push webhook on the repository, creating it or rewriting an existing one at the same URL. - The node deploy key is now registered for every node rather than only the first, and is skipped when already present. - `check_node` audits the box's consume-channel list and fails when it omits `edge`. ### Changed - Both node health probes use HTTPS and skip certificate verification on the loopback only. A probe aimed at a real hostname still verifies. - `post_deploy.sh` treats a missing `requirements.txt` as a logged skip. A project whose only dependency is django-mojo legitimately has none. - The generated secrets bundle backfills fields added by a later release, adding only what is missing and never re-minting an existing value. - The stage-1 payload gained a fourth object recording the commit the application tarball was archived from. - The shipped systemd units carry a `Documentation=` URL. ### Fixed - The canary rolled back every successful deploy. `sanity_check` probed `http://127.0.0.1/api/version`, the shipped `:80` vhost 301s everything except the ACME path, and the check does not follow redirects — so it saw the 301 and reported a failure. Releases survived only because the rollback failed too. - The post-restart health gate passed on that same redirect: `curl -f` does not fail on a 3xx, so nginx being alive satisfied a check meant to prove the application was. - `configure` rolled a deployed node back to its boot commit. The recorded boot SHA is now consulted only to adopt a tree that is not yet a checkout; after that the deploy plane owns HEAD. - `update.sh` could not write its own phase timings — the file was created root-owned by the other half of the deploy — so the phase table reaching the platform was missing the half that wraps the deploy. ### Upgrade notes - Run `provision apply` before `provision configure`. Apply is what mints the webhook secret, creates the KMS key and the releases bucket, and republishes the boot payload; configure only publishes and wires what apply produced. - A node provisioned before this release has no recorded boot commit, so `configure` reports it as a manual step and leaves it alone rather than guessing. Replace the node, or wire `/opt/api` to the repository by hand. - If you have edge upstreams pointing under `/run/mojo`, move them under `/var` or set `EDGE_SOCKET_BASE` yourself before upgrading. - A project that overrides `JOBS_CHANNELS` replaces the framework's list rather than extending it. Confirm yours still contains `edge`, or push-to-deploy answers 503 with no error logged anywhere.