cayce 90e6cfcc19 fix: address external audit — criticals 1-5 + mediums
Critical 1 (approval forgery): audit_log is now hash-chained
(prev_hash/entry_hash, verify_audit_chain); executor approve REQUIRES a
BIP-340 signature over sha256(cayce-ops:approve:<id>) when
CAYCE_OPS_OPERATOR_PUBKEY is set; deploy/roles.sql ships least-privilege
per-daemon DB roles.

Critical 2 (threading dead): sends create real OUTBOUND rows (own
Message-ID, In-Reply-To -> inbound); inbound message_id never overwritten;
maild compose adds the first-contact path; _match_proposal falls back to
inbound chains.

Critical 3 (duplicate sends): per-message transactions in send path —
SMTP failure on message N no longer resurrects 1..N-1; failures audited
(send_failed) and the row stays queued.

Critical 4 (classifier): negated approvals (cannot accept / not approved /
not accepting) classify REJECTION at 0.95 before the bare-keyword rule;
LLM prompt braces escaped (.format no longer KeyError into silent fallback).

Critical 5 (systemd): every daemon has a run subcommand
(maild run = poll+send, publisher run = publish-due, monitor run = build);
deploy README documents the separate daily publisher plan timer.

Mediums: approved_hash binds drafts/captions to the approved text (edited
text refuses to send/publish until re-approved); risk_score renamed
match_score (migration 0002) and auto-approve orders HIGHEST relevance
first; compose binds postgres to 127.0.0.1; IMAP marks \\Seen only after
commit (fetch returns (num, raw), mark post-commit); disabled channels are
terminal (ContentStatus.SKIPPED) and publish failures burn a bounded
attempt budget before abandonment; make hooks installs the pre-push gate.

tests: 12 new — 56 total, all green; ruff 0 findings; gitleaks clean.
2026-10-19 23:06:29 +00:00

Cayce is an agent-operated project run by Gustavo.

Cayce Ops is the autonomous operations stack behind Cayce — an agent that runs bitcoinhotels.org (Bitcoin-native travel guides) and hardcap.news (Bitcoin security intelligence).

Five decoupled components share one Postgres database. Each is a CLI command, driven by cron/systemd timers. Nothing runs without an audit trail.

Component What it does Human gate
scanner Discovers opportunities (affiliates, partnerships, content gaps) from configured sources; files them as proposals none — it only proposes
executor Runs approved proposals through registered action plugins; full audit of who approved, what ran, what happened approval required (AUTONOMOUS_MODE=false by default)
mail-negotiator Classifies merchant replies (approval / rejection / question / other) over IMAP; drafts replies; SMTP sends only human-approved drafts draft approval required
publisher Themed content calendar. Nostr (NIP-01) adapter first; Facebook/Instagram adapters exist but are disabled by default; brand images via Pillow templates publish requires approved items
monitor Static status page: daemon health, last runs, proposal queue, recent errors. No sensitive data none — read-only

State machines:

proposal: proposed -> approved -> running -> done | failed
mail:     inbound classified; draft: none -> drafted -> approved -> sent
content:  draft -> approved -> published

Quickstart (local)

uv sync --frozen            # installs Python 3.12 + locked deps
cp .env.example .env        # fill in; values are placeholders
docker compose up -d db     # Postgres 16
uv run alembic upgrade head
uv run cayceops scanner run
uv run pytest

docker compose up (everything) runs all five daemons in loops; on a VPS use deploy/systemd instead.

Layout

cayceops/            the package
  config.py          env-var config (CAYCE_OPS_*), no config files
  db.py              engine/session/upgrade helpers
  models.py          ORM schema + state machines + audit helper
  llm.py             provider-agnostic LLM interface (null by default)
  components/
    scanner/         discovery -> proposals (never executes)
    executor/        plugin execution + audit
    maild/           IMAP classify, drafts, SMTP gated on approval
    publisher/       calendar, nostr adapter, image templates
    monitor/         static status page builder
alembic/             migrations (production schema path)
tests/               pytest suite (invented fixtures only)
deploy/systemd/      VPS units + timers
docs/DECISIONS.md    architectural decisions and their costs

Security posture

  • Config exclusively via env vars; .env is gitignored, .env.example holds placeholders.
  • make check (and CI): ruff, pytest, alembic smoke, gitleaks over the full history.
  • SMTP send, social publish, and proposal execution all require a human action recorded in the audit log — unless AUTONOMOUS_MODE=true, which enables one narrow, capped, audited exception (see docs/DECISIONS.md D5).

License

MIT — see LICENSE.

S
Description
Cayce autonomous operations stack — scanner, executor, mail-negotiator, publisher, monitor. MIT.
Readme MIT
492 KiB
2026-10-19 23:34:24 +00:00
Languages
Python 64.7%
JavaScript 27.5%
Shell 3%
PLpgSQL 2.3%
TypeScript 2%
Other 0.4%