Spec 06 — Security
The threat model and its controls: sender identity, approval binding, credentials, secrets and incidents.
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.yamlis 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
structuralPRs.
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:
- Revoke/reduce the implicated sender or agent principal (
trust.yamlPR, fast-tracked). git logthe audit trail; identify affected merged PRs.- Revert-PR the affected commits; CI republishes.
- 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.