From 3bde7852613a3768842d111519307cf4aec328f1 Mon Sep 17 00:00:00 2001 From: Cayce Date: Tue, 20 Oct 2026 02:21:36 +0000 Subject: [PATCH] audit-7 + docs: plan systemd units, D13-D15 - cayce-ops-plan.service/.timer (daily): the @ template cannot express publisher plan, so it gets its own unit - DECISIONS: D13 no non-human approvals; D14 signatures bind content + deployment; D15 negation veto classifier --- deploy/systemd/cayce-ops-plan.service | 15 +++++++++++++++ deploy/systemd/cayce-ops-plan.timer | 10 ++++++++++ docs/DECISIONS.md | 22 ++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 deploy/systemd/cayce-ops-plan.service create mode 100644 deploy/systemd/cayce-ops-plan.timer diff --git a/deploy/systemd/cayce-ops-plan.service b/deploy/systemd/cayce-ops-plan.service new file mode 100644 index 0000000..e779dc4 --- /dev/null +++ b/deploy/systemd/cayce-ops-plan.service @@ -0,0 +1,15 @@ +[Unit] +Description=cayce-ops publisher plan (fills the content calendar, daily) +After=network-online.target postgresql.service +Wants=network-online.target + +[Service] +Type=oneshot +User=cayce +WorkingDirectory=/srv/cayce-ops +EnvironmentFile=/srv/cayce-ops/.env +ExecStart=/srv/cayce-ops/.venv/bin/cayceops publisher plan +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=/srv/cayce-ops diff --git a/deploy/systemd/cayce-ops-plan.timer b/deploy/systemd/cayce-ops-plan.timer new file mode 100644 index 0000000..2684063 --- /dev/null +++ b/deploy/systemd/cayce-ops-plan.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Run cayce-ops publisher plan daily + +[Timer] +OnCalendar=*-*-* 06:00:00 +RandomizedDelaySec=300 +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 71ea27b..8380ee1 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -95,3 +95,25 @@ caption hash-bound at approval, disabled channels are terminal (`SKIPPED`), publish failures burn a bounded attempt budget before abandonment. `risk_score` renamed `match_score` (keyword relevance) and auto-approval takes the HIGHEST score first. + +## D13 — No non-human approval path (round 2, supersedes D5) +Operator signatures are mandatory for approvals AND re-verified at +execution/send/publish time against current content + deployment id. With +that gate, the AUTONOMOUS_MODE auto-approval path cannot exist: it would +produce unsigned approvals the gate refuses. `_auto_approve` is inert and +audits `autonomy_disabled` when invoked. *Cost:* AUTONOMOUS_MODE is now +inert by design. + +## D14 — Signatures bind content + deployment (round 2) +message = sha256("cayce-ops::::"). +content_hash is canonical-json of the fields that matter per type. Full +signatures live on the row (operator_signature) and in the audit detail. +Verification happens again at run time: unsigned rows (raw SQL approval) +revert to their pre-approval state with an error audit. *Cost:* a deployment +id env var is mandatory; signatures must be recomputed if content changes. + +## D15 — Approval classifier veto (round 2) +Any negation token anywhere in a mail vetoes an `approval` classification → +needs_review. Simpler and strictly conservative vs. per-pattern negation +lists. *Cost:* genuinely-positive approvals containing stray negations +(e.g. "we have no reason not to welcome you") need human triage.