- hardcap PATCH /admin/articles/:id → PUBLISHED now stages into
outbound_drafts (channel hardcap) instead of publishing, when
HARDCAP_PUBLISH_MODE=draft (default). Fail-closed. Verified E2E:
PATCH on a QUEUED article returned {staged:true, draft_id:7} and the
article stayed QUEUED.
- bitcoinhotels.org: audited — frontend only (Next.js), no publishing
path; nothing to contain.
- outbox.mjs: self-sufficient pool (createRequire from
/srv/cayce-core/node_modules/pg — hosts may lack pg; found by the
promo simulation which now works end-to-end).
- runtime/hardcap/admin.ts: versioned copy of the patched route
(hardcap-backend itself is not a git repo).
- deploy.sh: hardcap env wiring (HARDCAP_PUBLISH_MODE, IDENTITY_PATH,
CAYCE_PGURL appended server-side).
🔴 UNRELATED CRITICAL FINDING (needs operator decision, not patched
without approval): hardcap admin API has NO authentication and binds
*:3001 — reachable from the public internet (HTTP 200 from outside,
including DELETE /admin/articles/all). Recommend: auth middleware +
bind 127.0.0.1 + firewall, plus rotation of credentials that appeared
in journald (hardcap DB role DSN leaked via a psql error line during
testing — fixed in the same patch, but journald retains it).
Problem: Cayce publishes straight to relays with no approval gate —
cayce-initiative publish_note, core.mjs digest/opinion/reflection/events,
mention auto-replies, social promo (Facebook/Instagram via sfeed), and
mail — while the operator believed approvals existed (they never did;
ADR-0005 Telegram queue was never implemented).
Mechanism (outbox.mjs, deployed to all runtimes):
- CAYCE_PUBLISH_MODE=draft (default): gateOutbound() stages EVERY outbound
artifact into outbound_drafts (entity DB) with identity_path +
identity_hash (sha256 of prompts/cayce-identity.md) before anything
else happens. Fail-closed: if staging fails, the event is discarded,
never published.
- CAYCE_PUBLISH_MODE=live: original behavior, unchanged (reversible with
one line per service .env).
- Chokepoint: the nostr daemon /publish endpoint gates ALL callers
(initiative publish_note, core.mjs ×4, banner kind 0); the daemon own
mention-reply path gates too, storing original_event for context;
startup profile publish gates as well. DM-to-supervisor (/dm) is
internal and stays live.
Identity (point 8b):
- prompts/cayce-identity.md: the 5 hardcoded voice blocks extracted
VERBATIM (verified byte-identical by runtime test) with block markers.
- core.mjs / editorials.mjs now load their voice blocks from this file
(loadIdentityBlock); identity hash recorded on every draft.
- runtime/ holds the versioned copies of the previously-unversioned
runtime scripts + deploy.sh installer.
No timers enabled or disabled. No production data modified.