1. Threat model

Threat Vector Primary control
Website defacement Spoofed/forwarded email impersonating an allowlisted sender §2 sender identity + §4 review gate; Constitution §6
Approval forgery Fabricated or replayed approval reply §3 nonce binding
Agent overreach Agent merges, edits chrome, or edits the dial unprompted §5 scoped credentials + branch protection
Malicious content Link/image injection in submitted content §6 content hygiene + review
Secret leakage Credentials in content or repo §7
Preview leak Draft content indexed or shared spec/05 §3 noindex + unguessable paths

2. Sender identity

  • The allowlist in trust.yaml is the only identity system. Display names mean nothing; addresses are matched exactly.
  • SPF/DMARC pass SHOULD be required on ingest where the infrastructure makes it available; it is defence in depth, never a substitute for the allowlist (forwarded mail may fail SPF legitimately — the allowlist decides, DMARC informs).
  • A request arriving from an unrecognised address gets a polite refusal and a log entry. It never enters the pipeline.

3. Approval binding

Normative from spec/04 §2, restated as a security property: an approval is a three-way binding — (allowlisted address, thread, nonce). Break any leg and the approval is void. Nonces are single-use, expiry-bounded, and generated inside the agent (never supplied by the requestor).

4. The human review gate

Constitution §4 is a security control, not just process: no credential configuration may permit a write to main outside the PR path. Branch protection: no force push, no direct push, required green build, required approvals per the dial. Agents hold credentials that can open PRs and update branches — never merge credentials, unless the dial configuration explicitly grants automated merge for a bounded class (L2), in which case the grant is per-site, versioned, and revocable.

5. Agent credentials

  • The editor agent authenticates as a principal distinct from any human (its own GitHub App/bot identity), so its actions are attributable in the audit trail.
  • Scope: read the repo; write branches; open/update PRs; comment. Nothing else. No admin, no settings, no secrets access beyond what the pipeline needs (SMTP send, storage deploy credentials are held by CI, not by the agent).
  • The agent’s own prompt surface (instructions, tool descriptions) is configuration in the repository and changes via structural PRs.

6. Content hygiene

  • All content is reviewable by construction (it is in a PR), so the review is the injection control. Reviewers check: external links point where they claim; images are site assets; submitted “HTML” in a blob is read as content.
  • Media files are type- and size-checked at build; the media schema requires alt text (spec/02 §6).
  • No forms are served from the site; external form services are linked, not embedded, unless a site explicitly opts into an embed block with reviewed provenance.

7. Secrets

Secrets (SMTP, deploy keys, API tokens) live in the CI platform’s secret store. The repository contains none. A build that discovers a secret-shaped string in content SHOULD fail (scan step) — a defence-in-depth measure.

8. Incident response

Suspected compromise or defacement:

  1. Revoke/reduce the implicated sender or agent principal (trust.yaml PR, fast-tracked).
  2. git log the audit trail; identify affected merged PRs.
  3. Revert-PR the affected commits; CI republishes.
  4. Record the incident in the site repository (an issue), never only in chat.

The property that makes this tractable: production is a projection of main, and main’s history is immutable — recovery is always a revert, never a forensic rebuild of unknown state.