From 9f2d83c32fc3e23c4cef187915ec2eedc57cc8c3 Mon Sep 17 00:00:00 2001 From: Pouya Lajevardi Date: Mon, 31 Aug 2026 10:59:12 -0400 Subject: [PATCH] fix: give the Gitea workflow the intake route check the local script has MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/deploy-local.sh's header requires the two deploy paths to match on everything that determines what gets published, and the route check that replaced the stale INTAKE_ENDPOINT guard had only been added to one of them. The check POSTs to /api/intake with no Origin header. 404 means the CloudFront /api/* behaviour is missing; 403 means routed and refused by the handler's own Origin check, which is a pass — and is why the probe is safe against production, since it is rejected before any DynamoDB write or any email. It warns rather than failing, because by that point the site is already deployed. docs/06 now records it on the cutover item it protects, so that item no longer rests on someone reading the list. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5 --- .gitea/workflows/deploy.yml | 34 ++++++++++++++++++++++++++++++++++ docs/06-deployment.md | 11 +++++++++++ 2 files changed, 45 insertions(+) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 037c107..78a0dd9 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -161,5 +161,39 @@ jobs: --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" \ --paths "/*" + # Mirrors the same step in scripts/deploy-local.sh, because that script's + # header requires the two paths to match on everything that determines + # what gets published - and this replaced the INTAKE_ENDPOINT guard. + # + # The contact form posts to the same-origin path /api/intake, which only + # works if a CloudFront behaviour routes /api/* to the HTTP API origin + # AGENTS.md §7 records. Nothing in the build can know whether it exists. + # + # 404 means not routed. 403 means routed and REFUSED, which is the correct + # answer here: the handler checks the Origin header and this request sends + # none, so it is rejected before any DynamoDB write or any email. That is + # why the probe is safe to run against production. + # + # It warns rather than failing: the site is already deployed by this point, + # and failing the job would not un-deploy it. + - name: Intake route check + run: | + code=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \ + --max-time 15 \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + --data 'deploy-route-probe=1' \ + "https://adr.smlcompany.ca/api/intake" || echo 000) + case "$code" in + 404|000) + echo "WARNING: POST /api/intake returned $code." + echo "The contact form posts there. 404 means the CloudFront /api/*" + echo "behaviour is missing; 000 means the request did not complete." + echo "See docs/06-deployment.md's cutover checklist." + ;; + *) + echo "POST /api/intake -> $code (routed; 403 is the Origin check)" + ;; + esac + - name: Summary run: echo "Deployed to https://adr.smlcompany.ca — commit ${GITHUB_SHA:0:7}" diff --git a/docs/06-deployment.md b/docs/06-deployment.md index 2018b07..d298fe3 100644 --- a/docs/06-deployment.md +++ b/docs/06-deployment.md @@ -447,6 +447,17 @@ Then invalidate `/*`. (`docs/05`). Not expressible in handler code, and it is carrying load the unimplementable 3-second timing check was specified to carry — see `docs/05` §Three deviations. + + ✅ **BOTH DEPLOY PATHS NOW WARN IF ITEM 1 IS MISSING**, so this does not + rest on someone reading this list. `scripts/deploy-local.sh` and + `.gitea/workflows/deploy.yml` each end with an **Intake route check**: a + `POST` to `/api/intake` with no `Origin`. **404 means not routed. 403 means + routed and refused**, which is the handler's `Origin` check doing its job + and is therefore a pass — and is why the probe is safe against production: + it is rejected before any DynamoDB write or any email. It warns rather than + failing, because by that point the site is already deployed and failing the + job would not un-deploy it. This replaced an `INTAKE_ENDPOINT` guard that + blocked deploys on a variable nothing read. - [ ] **DYNAMODB TTL ENABLED ON THE TABLE, AND VERIFIED BY A TEST RECORD. THIS ONE BACKS A PUBLISHED PROMISE AND MUST NOT BE TICKED FROM THE HANDLER CODE.** `/legal/privacy/` states that a record is *"deleted automatically