Clarify production handoff is not deployed

This commit is contained in:
codex committed 2026-10-31 00:58:56 +03:00
1 parent 3d267afaee
commit 96ed0949ca
5 files changed
+69 -6

No files matched your search

+9 -2
View File
@@ -1,4 +1,4 @@
name: Prepare production deploy handoff
name: Production handoff status (not deployed)
on:
workflow_run:
@@ -7,7 +7,8 @@ on:
types: [completed]
jobs:
validate-handoff:
deployment-status:
name: NOT DEPLOYED - handoff status only
if: >-
gitea.event.workflow_run.conclusion == 'success' &&
gitea.event.workflow_run.event == 'workflow_run' &&
@@ -55,3 +56,9 @@ jobs:
trap 'rm -f "$metadata"' EXIT
gitea-api GET "/repos/admin/akyldash/actions/runs/${SOURCE_RUN_ID}/artifacts" > "$metadata"
python3 infra/deploy/release_handoff.py dry-run --artifact-metadata "$metadata"
- name: State that no deployment was performed
run: |
echo 'DEPLOYMENT STATUS: NOT DEPLOYED'
echo 'This workflow checks source provenance and may inspect artifact metadata only.'
echo 'It does not download the descriptor ZIP, open SSH, or invoke the Windows receiver.'
echo 'No Windows execution runner or automatic runtime binding has been verified.'
+10 -4
View File
@@ -214,10 +214,16 @@ Docker state, UFW rule details, and protected file metadata were not verified.
The descriptor remains unsigned trace metadata, not cryptographic
attestation.
- A separate `deploy-handoff.yaml` runs after a successful release and checks
the source run and stale-main guards. It is dry-run only. It reads artifact
metadata through `gitea-api` only if that helper is available in the job
image; otherwise it reports metadata and host handoff as unverified. It does
not download artifact ZIPs, connect to SSH, or apply a release. The original
the source run and stale-main guards. Its job is explicitly
`NOT DEPLOYED - handoff status only`; a green result is not a deployment. It
reads artifact metadata through `gitea-api` only if that helper is available
in the job image; otherwise it reports metadata and host handoff as
unverified. It does not download artifact ZIPs, connect to SSH, or apply a
release. No repository workflow binding to a Windows runtime starts the receiver;
external Windows runner/service inventory has not been verified. The client
currently requires an explicit process running `receive_external.py` with
the release tuple to open the SSH session. Image release is automated, but
production deployment is not yet automatic CD. The original
audited `gitea-api` helper lacks binary output and safe 302 handling; the
current PR carries a tested patch and host-side artifact verifier, but
neither is installed. No deploy key, fixed SSH entry, extra sudo rule, or
+32
View File
@@ -35,6 +35,38 @@ the response, and refuses to overwrite an existing file. The installer checks
the exact audited helper fixture hash before applying its patch. The helper
and source code are not installed on the VPS by this PR.
## Automation boundary: deployment is not configured
The automatic chain currently ends at release publication:
1. Successful `main` CI triggers image builds and publishes digest references
plus the release descriptor artifact.
2. `deploy-handoff.yaml` verifies the source run and may read artifact metadata.
Its job is explicitly status-only: **NOT DEPLOYED**. It does not download
the descriptor ZIP, call the Windows client, open SSH, or invoke the root
receiver. A green result means only that this status check completed.
3. The fixed receiver is physically started only when a Windows process runs
`infra/backup/receive_external.py` with the release run ID, commit, and
descriptor digest. That command opens the owner's SSH session to the fixed
root receiver. No repository workflow binding starts it; external Windows
runner/service inventory has not been verified.
Therefore production CD is not yet automatic. A runner label only routes a
job; it does not prevent a pull-request workflow from requesting that label.
Before unattended cutover, the owner must choose an execution design that
demonstrably prevents untrusted PR jobs from reaching the Windows host. A
dedicated persistent host-mode runner with D: data and SSH access is unsafe
while general repository PR workflows can target it. Acceptable designs
include an isolated one-job executor exposed only after an external controller
validates the trusted release event and exact run/commit/artifact tuple, or a
separate protected deployment repository with no untrusted PR trigger/write
path. In either design, the fixed receiver must report its ACK and SSH result
before deployment is marked successful. Gitea 1.25 documents Windows runner
installation and service operation in its
[act_runner guide](https://docs.gitea.com/1.25/usage/actions/act_runner).
No such isolation/controller, Windows runner, registration, token, service,
SSH credential, or workflow binding has been verified or installed by this PR.
## External backup and cutover
The fixed root release receiver accepts only a bounded protocol request over
@@ -6,6 +6,18 @@ private keys, secret material, or raw process environments.
## Required before release use
- Decide whether unattended production CD is acceptable. It currently is not
configured: `deploy-handoff.yaml` is metadata/status-only and reports
`NOT DEPLOYED`; the Windows receiver starts only from an explicit owner-run
CLI command. Automatic activation requires a secure Windows execution path
available only to a validated trusted release. A `runs-on` label is routing,
not authorization; a persistent host-mode runner sharing D: and SSH files
must not be exposed to general repository PR workflows. Before enabling CD,
choose an isolated one-job executor gated by an external validator, or a
separately protected deployment repository with no untrusted PR trigger or
write path. Verify that it validates repository, workflow, event, branch,
run, commit, and artifact digest, then reports receiver ACK and SSH success.
No such runtime isolation or binding exists in this PR.
- Confirm the registry package names and least-privilege writer/pull access for
`admin/akyldash/backend` and `admin/akyldash/opensearch`; record digest
retention for the rollback window.
+6
View File
@@ -121,6 +121,8 @@ class ReleaseWorkflowTests(unittest.TestCase):
def test_deploy_handoff_is_dry_and_uses_the_source_release_run_id(self):
workflow = (ROOT / ".gitea/workflows/deploy-handoff.yaml").read_text(encoding="utf-8")
self.assertIn("name: Production handoff status (not deployed)", workflow)
self.assertIn("name: NOT DEPLOYED - handoff status only", workflow)
self.assertIn('workflows: ["Release production images"]', workflow)
self.assertIn("branches: [main]", workflow)
self.assertIn("types: [completed]", workflow)
@@ -134,6 +136,10 @@ class ReleaseWorkflowTests(unittest.TestCase):
self.assertNotIn("AKYLDASH_DEPLOY_SSH_KEY", workflow)
self.assertNotIn("docker.sock", workflow)
self.assertIn("host handoff are not verified", workflow)
self.assertIn("DEPLOYMENT STATUS: NOT DEPLOYED", workflow)
self.assertIn("No Windows execution runner or automatic runtime binding has been verified.", workflow)
self.assertNotIn("receive_external.py", workflow)
self.assertNotIn("akyldash-production-windows", workflow)
def test_source_guards_reject_wrong_event_or_stale_main(self):
valid = dict(