fix: sweep D3 amendment through the specs; correct inverted DKIM table
The re-audit of the deploy-guard change surfaced defects well outside the diff, including one that would have broken production mail. docs/05-backend-spec.md had the two SES DKIM sets exactly inverted, labelling the three records that resolve as "orphans" and the three NXDOMAIN records as "Live. Never delete". Entry (j) corrected this in AGENTS.md §7 and the correction never reached docs/05. Since SES has no custom MAIL FROM, DKIM is the only thing satisfying DMARC, so acting on that table would have silently broken intake mail authentication. Also in this change: - .gitea/workflows/deploy.yml gains a guard as steps[0] that fails the run, naming the variable, if AWS_REGION, S3_BUCKET or CLOUDFRONT_DISTRIBUTION_ID is empty — how a Gitea too old for the vars context manifests. Verified fail-closed under bash -e, sh -e and bash -euo pipefail. - AGENTS.md Current Truth: SPF and DMARC recorded as present (Q20), the matching §10 High risk row retired, three duplicate Q rows removed. - docs/reference/AWS-Hosting-Guide.md tracked and given a do-not-execute banner; it was an executable procedure for the architecture D1/D3 replace. - Copy decks: "a working litigator" and "an active litigation practice" replaced with the register's own wording; LegalService JSON-LD replaced with ProfessionalService; tribunal-secretary offers removed per D14; nine stale question blockers swept. - astro.config.mjs: prefetchAll disabled — it injected JS into every page against the zero-JS convention with no decision recorded. - src/data/site.ts: unregistered response-time commitment nulled (Q27); OBA section names downgraded to [assumed] (Q28). - s3:AbortMultipartUpload reasoning corrected to measure ./dist, not the repo. Opens Q27, Q28, Q29. AGENTS.md entry (q) records the full resolution, including the findings declined and why. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XquaEq4BgWMCwUqLEyNkF
This commit is contained in:
co-authored by
Claude Opus 5
parent
e6abdf42e8
commit
6bf1167624
+31
-26
@@ -1,7 +1,9 @@
|
||||
# 05 — Intake, booking, and data handling
|
||||
|
||||
Authority: `AGENTS.md` §3 D10 — rebuilt intake form plus calendar booking.
|
||||
Existing infrastructure is documented in `AWS-Hosting-Guide.md` Parts 8–10.
|
||||
Existing infrastructure is authoritative in `AGENTS.md` §7. How it was built is
|
||||
recorded in `docs/reference/AWS-Hosting-Guide.md` Parts 8–10 — a historical
|
||||
record with a do-not-execute banner, superseded by §7 wherever they disagree.
|
||||
**Read that guide before changing anything**; the resources already exist and
|
||||
were built by hand in the console.
|
||||
|
||||
@@ -10,7 +12,7 @@ were built by hand in the console.
|
||||
## What exists today
|
||||
|
||||
API Gateway (HTTP API) → Lambda → DynamoDB, with SES for notification email and
|
||||
a verified sender on `smlcompany.ca`. `[verified 2026-08-25 — AWS-Hosting-Guide.md]`
|
||||
a verified sender on `smlcompany.ca`. `[verified 2026-08-26 — AGENTS.md §7]`
|
||||
|
||||
The shape is right. This is a hardening and rework pass, not a replacement.
|
||||
|
||||
@@ -73,7 +75,8 @@ Client-side validation is a convenience. **The Lambda re-validates everything.**
|
||||
|
||||
DynamoDB, `ca-central-1` — **Canadian data residency is a real selling point for
|
||||
a Canadian legal practice, and the privacy policy will say so.** Confirm the
|
||||
existing table's region and migrate if it is elsewhere (**Q10**).
|
||||
existing table's region and migrate if it is elsewhere. `AGENTS.md` §7 records
|
||||
`adr-intake-submissions` in `ca-central-1` `[verified 2026-08-26]`.
|
||||
|
||||
| Attribute | |
|
||||
|---|---|
|
||||
@@ -106,33 +109,33 @@ SES on submission:
|
||||
of the no-retainer language, and a link to the privacy policy. This email is
|
||||
the reason the form beats a `mailto:` link.
|
||||
|
||||
**Email authentication — verified 2026-08-26, and it is not in place.**
|
||||
**Email authentication — in place as of 2026-08-26 (Q20).**
|
||||
|
||||
A DNS query of `smlcompany.ca` found **no SPF record and no DMARC record**. Mail
|
||||
is on Google Workspace (MX `1 smtp.google.com`) with Google DKIM configured, and
|
||||
the SES domain identity reports verified for sending — but neither SPF nor DMARC
|
||||
exists.
|
||||
|
||||
**What is already in place** (Namecheap DNS and the SES console, both inspected
|
||||
2026-08-26):
|
||||
|
||||
| Record | Status |
|
||||
|---|---|
|
||||
| SES DKIM — `3zsnvsjg…`, `jejgp7na3…`, `xpiwyftpo…` `._domainkey` | **Live.** Matches SES exactly. Never delete |
|
||||
| SES DKIM — `f5puwearz…`, `jdue2r22c…`, `kznn3cklv…` `._domainkey` | Orphans from an earlier verification. Inert. **Leave them** — deleting the wrong three breaks DKIM |
|
||||
| `google._domainkey` TXT | Google Workspace DKIM. Never delete |
|
||||
| Two CNAMEs → `jkddzztszm.acm-validations.aws` | ACM certificate validation. **Never delete** — breaks HTTPS at the next renewal |
|
||||
| `adr` CNAME → `d26v23dhgsp2ta.cloudfront.net` | The site |
|
||||
| Custom MAIL FROM | **Not configured.** Optional; would add SPF alignment |
|
||||
|
||||
Add both of these; neither conflicts with anything above:
|
||||
SPF and DMARC were both added by Pouya and independently verified on
|
||||
2026-08-26. Mail is on Google Workspace (MX `1 smtp.google.com`) with Google
|
||||
DKIM configured, and the SES domain identity is verified for sending in
|
||||
`ca-central-1`. **An earlier version of this spec said neither record existed;
|
||||
that was true when written and is no longer.** The records now live are:
|
||||
|
||||
| Host | Type | Value |
|
||||
|---|---|---|
|
||||
| `@` | TXT | `v=spf1 include:_spf.google.com include:amazonses.com ~all` |
|
||||
| `_dmarc` | TXT | `v=DMARC1; p=none; rua=mailto:info@smlcompany.ca; fo=1` |
|
||||
|
||||
A domain may publish **only one** `v=spf1` record, so both senders go in one
|
||||
**What is already in place** (Namecheap DNS and the SES console, both inspected
|
||||
2026-08-26):
|
||||
|
||||
| Record | Status |
|
||||
|---|---|
|
||||
| SES DKIM — `f5puwearz…`, `jdue2r22c…`, `kznn3cklv…` `._domainkey` | **LIVE. Never delete.** All three resolve (NOERROR) and back the healthy `ca-central-1` SES identity — `DkimStatus: SUCCESS`. These are the records DMARC alignment rests on `[verified 2026-08-26 — DNS, AGENTS.md §7]` |
|
||||
| SES DKIM — `3zsnvsjg…`, `jejgp7na3…`, `xpiwyftpo…` `._domainkey` | **BROKEN and inert.** Entered into Namecheap with the full name in the Host field, so the zone doubled the domain; they answer NXDOMAIN at the correct name. They belong to a stray `us-east-1` identity this project does not use. Harmless where they are — leaving them is the low-risk choice (Q21) `[verified 2026-08-26 — DNS, AGENTS.md §7]` |
|
||||
| `google._domainkey` TXT | Google Workspace DKIM. Never delete |
|
||||
| Two CNAMEs → `jkddzztszm.acm-validations.aws` | ACM certificate validation. **Never delete** — breaks HTTPS at the next renewal |
|
||||
| `adr` CNAME → `d26v23dhgsp2ta.cloudfront.net` | The site |
|
||||
| Custom MAIL FROM | **Not configured.** Optional; would add SPF alignment |
|
||||
|
||||
Notes that mattered when these were added, kept because they matter again on
|
||||
any future edit: a domain may publish **only one** `v=spf1` record, so both senders go in one
|
||||
string. Namecheap TXT values take **no surrounding quotes** — quoting them stores
|
||||
the quotes literally and breaks the record.
|
||||
|
||||
@@ -140,7 +143,8 @@ the quotes literally and breaks the record.
|
||||
SES here. Without a custom MAIL FROM domain, SES uses an envelope sender at
|
||||
`amazonses.com`, so its SPF pass is not *aligned* with `smlcompany.ca` and does
|
||||
not satisfy DMARC. **SES satisfies DMARC through DKIM alignment** — that is what
|
||||
the six CNAMEs above are doing, and it already works. The SPF record's real job
|
||||
the **three resolving** DKIM CNAMEs above are doing, and it already works. (Six
|
||||
are present in the zone; only the `f5pu` / `jdue` / `kznn` set answers.) The SPF record's real job
|
||||
is authenticating **Google Workspace** mail, which currently has no SPF at all.
|
||||
`include:amazonses.com` is harmless and becomes useful if a custom MAIL FROM
|
||||
domain is configured later.
|
||||
@@ -152,7 +156,7 @@ no visibility.
|
||||
|
||||
**Do not delete the ACM validation CNAMEs.** They are how the certificate for
|
||||
`adr.smlcompany.ca` auto-renews. Removing them breaks HTTPS at the next renewal
|
||||
— silently, months later (Q20).
|
||||
— silently, months later.
|
||||
|
||||
Failure handling: SES failure must never lose the submission. Write to DynamoDB
|
||||
first, then send. A dead-letter queue on the Lambda, and a CloudWatch alarm on
|
||||
@@ -204,7 +208,8 @@ relationship · cookie and analytics disclosure · last-updated date.
|
||||
|
||||
If analytics ship, prefer a cookieless privacy-preserving tool (Plausible,
|
||||
Fathom). GA4 on a page collecting legal-dispute information is a poor fit for a
|
||||
practice whose privacy posture is part of its offer (**Q11**).
|
||||
practice whose privacy posture is part of its offer — D15 settles this:
|
||||
Plausible or Fathom, cookieless, no consent banner.
|
||||
|
||||
## Definition of done
|
||||
|
||||
|
||||
Reference in New Issue
Block a user