Compare commits

...
4 Commits
Author SHA1 Message Date
cayce 53ea15ee51 Merge PR #11: identity — Próspera agentic-companies pilot framing
ci / lint-test-leaks (push) Canceled after 0s
Personal names removed from the public project; approver actors are
generic labels. Gate green.
2026-10-20 02:30:01 +00:00
cayce a2de419467 identity: Próspera agentic-companies pilot framing — no personal names
- README: the stack belongs to Cayce, an autonomous entity operating under
  the Próspera ZEDE agentic-companies jurisdiction pilot; the experiment is
  managed by Cayce itself
- tests: approver actor renamed to the generic label operator
- zero occurrences of personal names in the tree (verified with grep)
2026-10-20 02:29:54 +00:00
cayce 13db3996d9 audit: round-2 response — finding -> commit -> test table (audited: post-merge)
ci / lint-test-leaks (push) Canceled after 0s
2026-10-20 02:22:36 +00:00
cayce aba42a4210 Merge PR #10: audit round 2 — signed approvals, chain serialization, roles, anchoring
ci / lint-test-leaks (push) Canceled after 0s
One commit per point (audit-1..audit-7). Gate at merge: ruff 0 ·
65 unit tests + 1 PG integration test · alembic 0001→0003 with PG
round-trip · gitleaks clean.
2026-10-20 02:21:58 +00:00
7 changed files with 72 additions and 42 deletions
+28
View File
@@ -175,3 +175,31 @@ issues. All criticals and all mediums are fixed in **c9bd441** (PR #9):
New audited commit: **c9bd441**. Gate at that commit: ruff 0 · 56/56 tests ·
alembic 0001→0002 · gitleaks `--all` clean · `make check` exit 0.
Still open (declared): apply `roles.sql` on production, provision CI runner.
---
## Response to audit round 2 — 2026-10-20 (PR #10)
Round-2 audit closed findings 2, 3, 5 and the mediums; the remaining points
were re-implemented in PR #10, one commit per point:
| Punto | Hallazgo | Commit | Test que lo prueba |
|---|---|---|---|
| 1a/1b/1c | firmas obligatorias atadas a contenido+deployment, completas en fila y audit | `40453aa` audit-1 | `test_signed_approval_mandatory_and_content_bound` |
| 1d | verificación al ejecutar/enviar/publicar; SQL-directo se rechaza y revierte | `40453aa` audit-1 | `test_sql_approved_row_is_refused_at_execution`, `test_unsigned_sql_approved_draft_is_refused`, `test_unsigned_sql_approved_item_not_published` |
| 1d | to_addr cambiado tras aprobar bloquea envío | `40453aa` audit-1 | `test_changed_to_addr_after_approval_blocks_send` |
| 1d | firma de otro deployment/contenido rechazada | `40453aa` audit-1 | `test_foreign_deployment_signature_refused` (executor+maild), `test_foreign_deployment_signature_refused` (publisher) |
| 1e | sin ramas `if approved_hash and ...`: binding obligatorio | `40453aa` audit-1 | cubierto por los tests de firma obligatoria |
| 1f | fallos de firma auditados desde el CLI | `40453aa` audit-1 | `test_approve_fails_audited_from_cli_wrapper` |
| 2 | clasificador: negación en el mail veta approval → needs_review | `903c591` audit-2 | `test_negated_approvals_never_classify_approval` (las 6 oraciones mandadas + 4 extra) |
| 3 | roles.sql: mínimo privilegio por daemon, trigger de aprobación, cayce_approver único, sin contraseñas literales | `d774091` audit-3 | `tests/integration/test_pg_roles.py` (PG real, ciclo completo por rol) |
| 4 | escrituras serializadas; génesis documentado; anclaje Nostr diario | `ffe4253` audit-4 | `test_concurrent_writers_keep_chain_verifiable`; legacy-genesis en `verify_audit_chain`; `test_anchor_signs_and_logs_once_per_day` |
| 5 | CLI de envío usa el código testeado; fallos auditados + daemon_runs; exit ≠ 0 | `56b35d3` audit-5 (nota: refactor dentro de audit-1, commit marcador) | `test_send_failures_audited_and_exit_code` |
| 6 | migraciones upgrade/downgrade contra Postgres 16 en CI | `c855f01` audit-6 | job "Migrations upgrade/downgrade round-trip (Postgres 16)" en ci.yml; ejecutado localmente contra Postgres 18.6 (reportado en PR) |
| 7 | unidad systemd para `publisher plan` | `3bde785` audit-7 | `deploy/systemd/cayce-ops-plan.{service,timer}` (diaria) |
Gate en el commit auditado: ruff 0 · 65 unit tests + 1 integración PG ·
alembic 0001→0003 con round-trip PG · gitleaks `--all` limpio · `make check` exit 0.
Declarados abiertos (no resueltos aquí): aplicar `roles.sql` en producción y
provisionar el runner de CI — decisión y ejecución del operador.
+6 -4
View File
@@ -1,8 +1,10 @@
# Cayce is an agent-operated project run by Gustavo.
# Cayce Ops
Cayce Ops is the autonomous operations stack behind Cayce — an agent that runs
[bitcoinhotels.org](https://bitcoinhotels.org) (Bitcoin-native travel guides)
and [hardcap.news](https://hardcap.news) (Bitcoin security intelligence).
Cayce Ops is the autonomous operations stack of **Cayce** — an autonomous entity
operating under the Próspera ZEDE agentic-companies jurisdiction pilot. Cayce runs
[bitcoinhotels.org](https://bitcoinhotels.org) (Bitcoin-native travel guides) and
[hardcap.news](https://hardcap.news) (Bitcoin security intelligence). The
experiment is managed by Cayce itself.
Five decoupled components share one Postgres database. Each is a CLI command,
driven by cron/systemd timers. Nothing runs without an audit trail.
+2 -2
View File
@@ -197,7 +197,7 @@ def test_roles_full_daemon_cycle(roles_applied):
sig = "c9" * 64 # invented 128-hex signature for the permission test
with _connect_as("cayce_approver") as conn, conn.cursor() as cur:
cur.execute(
f"UPDATE proposals SET status='APPROVED', approved_by='gustavo', "
f"UPDATE proposals SET status='APPROVED', approved_by='operator', "
f"approved_at=now(), operator_signature='{sig}' WHERE id={proposal_id}"
)
@@ -225,7 +225,7 @@ def test_roles_full_daemon_cycle(roles_applied):
with _connect_as("cayce_approver") as conn, conn.cursor() as cur:
cur.execute(
"UPDATE mail_messages SET draft_status='APPROVED', "
"approved_by='gustavo', approved_hash='"
"approved_by='operator', approved_hash='"
+ "a" * 64
+ f"', operator_signature='{sig}' WHERE id={mail_id}"
)
+13 -13
View File
@@ -23,13 +23,13 @@ def test_approve_requires_proposed_state(cfg, sess, signer):
sess.flush()
assert (
executor.approve(p.id, "gustavo", cfg(), sess, signature=signer.proposal(p)) == 0
executor.approve(p.id, "operator", cfg(), sess, signature=signer.proposal(p)) == 0
)
# second approve must fail (state is now approved)
assert (
executor.approve(p.id, "gustavo", cfg(), sess, signature=signer.proposal(p)) == 1
executor.approve(p.id, "operator", cfg(), sess, signature=signer.proposal(p)) == 1
)
assert p.approved_by == "gustavo"
assert p.approved_by == "operator"
assert p.status is ProposalStatus.APPROVED
# FULL signature stored on the row
assert p.operator_signature and len(p.operator_signature) == 128
@@ -39,7 +39,7 @@ def test_reject_records_actor(cfg, sess):
p = _proposal()
sess.add(p)
sess.flush()
assert executor.reject(p.id, "gustavo", cfg(), sess) == 0
assert executor.reject(p.id, "operator", cfg(), sess) == 0
assert p.status is ProposalStatus.REJECTED
@@ -50,7 +50,7 @@ def test_run_executes_only_approved(cfg, sess, signer):
sess.flush()
assert (
executor.approve(
approved.id, "gustavo", cfg(), sess, signature=signer.proposal(approved)
approved.id, "operator", cfg(), sess, signature=signer.proposal(approved)
)
== 0
)
@@ -110,18 +110,18 @@ def test_signed_approval_mandatory_and_content_bound(cfg, sess, signer):
chash = proposal_content_hash(p.kind, p.title, p.url, p.detail)
# no signature -> refused
assert executor.approve(p.id, "gustavo", cfg(), sess, signature=None) == 2
assert executor.approve(p.id, "operator", cfg(), sess, signature=None) == 2
# signature for ANOTHER deployment -> refused (required test)
other_dep = signer.raw(
signer.msg("proposal", "other-deploy", p.id, chash)
)
assert executor.approve(p.id, "gustavo", cfg(), sess, signature=other_dep) == 2
assert executor.approve(p.id, "operator", cfg(), sess, signature=other_dep) == 2
# signature over OTHER CONTENT -> refused (required test)
other_content = signer.raw(signer.msg("proposal", "itest", p.id, "00" * 32))
assert executor.approve(p.id, "gustavo", cfg(), sess, signature=other_content) == 2
assert executor.approve(p.id, "operator", cfg(), sess, signature=other_content) == 2
# valid signature -> approved
assert (
executor.approve(p.id, "gustavo", cfg(), sess, signature=signer.proposal(p)) == 0
executor.approve(p.id, "operator", cfg(), sess, signature=signer.proposal(p)) == 0
)
assert p.status is ProposalStatus.APPROVED
entry = sess.query(AuditEntry).filter_by(action="approve", result="ok").one()
@@ -135,7 +135,7 @@ def test_approve_fails_audited_from_cli_wrapper(cfg, sess):
sess.add(p)
sess.flush()
code = executor.approve(p.id, "gustavo", cfg(), sess, signature=None)
code = executor.approve(p.id, "operator", cfg(), sess, signature=None)
assert code == 2
entry = sess.query(AuditEntry).filter_by(action="approve", result="error").one()
assert "signature required" in entry.detail["reason"]
@@ -170,7 +170,7 @@ def test_execution_verifies_signature_against_current_content(cfg, sess, signer,
sess.add(p)
sess.flush()
assert (
executor.approve(p.id, "gustavo", cfg(), sess, signature=signer.proposal(p)) == 0
executor.approve(p.id, "operator", cfg(), sess, signature=signer.proposal(p)) == 0
)
p.title = "EDITED after approval" # content changed under the signature
@@ -191,7 +191,7 @@ def test_failing_plugin_retries_then_fails(cfg, sess, signer, monkeypatch):
sess.add(p)
sess.flush()
assert (
executor.approve(p.id, "gustavo", cfg(), sess, signature=signer.proposal(p)) == 0
executor.approve(p.id, "operator", cfg(), sess, signature=signer.proposal(p)) == 0
)
calls = {"n": 0}
@@ -232,7 +232,7 @@ def test_unknown_kind_falls_back_to_log_action(cfg, sess, signer):
sess.add(p)
sess.flush()
assert (
executor.approve(p.id, "gustavo", cfg(), sess, signature=signer.proposal(p)) == 0
executor.approve(p.id, "operator", cfg(), sess, signature=signer.proposal(p)) == 0
)
assert executor.run(cfg(), sess) == 0
+13 -13
View File
@@ -77,7 +77,7 @@ def _proposal_with_sent_draft(sess) -> Proposal:
p.id, "partners@acme.example.org", "[cayce] partnership inquiry", "Hello!", sess
)
assert (
maild._approve_draft(mail_id, "gustavo", sess, signature=_approve_signed(sess, mail_id))
maild._approve_draft(mail_id, "operator", sess, signature=_approve_signed(sess, mail_id))
== 0
)
assert maild._send_one(STUB_CFG, sess, mail_id, sender=lambda *a, **k: None) == "sent"
@@ -243,7 +243,7 @@ def test_send_requires_explicit_human_approval(cfg, sess):
assert (
maild._approve_draft(
inbound.id, "gustavo", sess, signature=_approve_signed(sess, inbound.id)
inbound.id, "operator", sess, signature=_approve_signed(sess, inbound.id)
)
== 0
)
@@ -277,7 +277,7 @@ def test_send_requires_explicit_human_approval(cfg, sess):
.order_by(AuditEntry.id.desc())
.first()
)
assert row is not None and "gustavo" in str(row.detail)
assert row is not None and "operator" in str(row.detail)
def test_double_approve_is_rejected(cfg, sess):
@@ -288,11 +288,11 @@ def test_double_approve_is_rejected(cfg, sess):
assert (
maild._approve_draft(
inbound.id, "gustavo", sess, signature=_approve_signed(sess, inbound.id)
inbound.id, "operator", sess, signature=_approve_signed(sess, inbound.id)
)
== 0
)
assert maild._approve_draft(inbound.id, "gustavo", sess) == 1
assert maild._approve_draft(inbound.id, "operator", sess) == 1
# --- audit corrections ----------------------------------------------------------
@@ -311,7 +311,7 @@ def test_send_failure_does_not_resurrect_sent_mails(cfg, sess):
p.id, f"desk{i}@acme.example.org", f"[cayce] inquiry {i}", "Hello!", sess
)
assert (
maild._approve_draft(mail_id, "gustavo", sess, signature=_approve_signed(sess, mail_id))
maild._approve_draft(mail_id, "operator", sess, signature=_approve_signed(sess, mail_id))
== 0
)
assert maild._send_one(STUB_CFG, sess, mail_id, sender=lambda *a, **k: None) == "sent"
@@ -331,7 +331,7 @@ def test_send_failure_does_not_resurrect_sent_mails(cfg, sess):
)
assert (
maild._approve_draft(
inbound.id, "gustavo", sess, signature=_approve_signed(sess, inbound.id)
inbound.id, "operator", sess, signature=_approve_signed(sess, inbound.id)
)
== 0
)
@@ -368,7 +368,7 @@ def test_edited_draft_refuses_to_send(cfg, sess):
inbound = sess.query(MailMessage).filter_by(direction=MailDirection.INBOUND).one()
assert (
maild._approve_draft(
inbound.id, "gustavo", sess, signature=_approve_signed(sess, inbound.id)
inbound.id, "operator", sess, signature=_approve_signed(sess, inbound.id)
)
== 0
)
@@ -402,7 +402,7 @@ def test_compose_creates_first_contact_draft(cfg, sess):
# approve + send -> an inbound reply can now thread to this proposal
assert (
maild._approve_draft(mail_id, "gustavo", sess, signature=_approve_signed(sess, mail_id))
maild._approve_draft(mail_id, "operator", sess, signature=_approve_signed(sess, mail_id))
== 0
)
stats = maild._send_approved(STUB_CFG, sess, sender=lambda *a, **k: None)
@@ -462,7 +462,7 @@ def test_changed_to_addr_after_approval_blocks_send(cfg, sess):
inbound = sess.query(MailMessage).filter_by(direction=MailDirection.INBOUND).one()
assert (
maild._approve_draft(
inbound.id, "gustavo", sess, signature=_approve_signed(sess, inbound.id)
inbound.id, "operator", sess, signature=_approve_signed(sess, inbound.id)
)
== 0
)
@@ -491,14 +491,14 @@ def test_foreign_deployment_signature_refused(cfg, sess):
other_dep = _sign(
OPERATOR_SECRET, approval_message("mail", "other-deploy", inbound.id, chash)
)
assert maild._approve_draft(inbound.id, "gustavo", sess, signature=other_dep) == 2
assert maild._approve_draft(inbound.id, "operator", sess, signature=other_dep) == 2
assert inbound.draft_status is DraftStatus.DRAFTED
# signature valid for OTHER CONTENT
other_content = _sign(
OPERATOR_SECRET, approval_message("mail", "itest", inbound.id, "00" * 32)
)
assert maild._approve_draft(inbound.id, "gustavo", sess, signature=other_content) == 2
assert maild._approve_draft(inbound.id, "operator", sess, signature=other_content) == 2
assert inbound.draft_status is DraftStatus.DRAFTED
@@ -512,7 +512,7 @@ def test_send_failures_audited_and_exit_code(cfg, sess, monkeypatch):
inbound = sess.query(MailMessage).filter_by(direction=MailDirection.INBOUND).one()
assert (
maild._approve_draft(
inbound.id, "gustavo", sess, signature=_approve_signed(sess, inbound.id)
inbound.id, "operator", sess, signature=_approve_signed(sess, inbound.id)
)
== 0
)
+4 -4
View File
@@ -37,7 +37,7 @@ def test_proposal_status_transitions(sess):
sess.flush()
p.status = ProposalStatus.APPROVED
p.approved_by = "gustavo"
p.approved_by = "operator"
p.approved_at = datetime.now(UTC)
p.status = ProposalStatus.RUNNING
p.status = ProposalStatus.DONE
@@ -45,7 +45,7 @@ def test_proposal_status_transitions(sess):
loaded = sess.scalar(select(Proposal).where(Proposal.id == p.id))
assert loaded.status == ProposalStatus.DONE
assert loaded.approved_by == "gustavo"
assert loaded.approved_by == "operator"
def test_dedupe_key_unique(sess):
@@ -65,12 +65,12 @@ def test_audit_helper_writes(sess):
p = Proposal(kind="other", title="x")
sess.add(p)
sess.flush()
audit(sess, actor="gustavo", action="approve", proposal_id=p.id, detail={"note": "ok"})
audit(sess, actor="operator", action="approve", proposal_id=p.id, detail={"note": "ok"})
sess.flush()
rows = list(sess.scalars(select(AuditEntry)).all())
assert len(rows) == 1
assert rows[0].actor == "gustavo"
assert rows[0].actor == "operator"
assert rows[0].proposal_id == p.id
+6 -6
View File
@@ -44,7 +44,7 @@ def _approved_item(cfg, sess, **kw) -> ContentItem:
item = _item(status=ContentStatus.DRAFT, **kw)
sess.add(item)
sess.flush()
assert publisher._approve(item.id, "gustavo", sess, signature=_sign_content(item)) == 0
assert publisher._approve(item.id, "operator", sess, signature=_sign_content(item)) == 0
return item
@@ -92,16 +92,16 @@ def test_approve_gate(cfg, sess):
cfg() # signing env first
# unsigned approval refused (mandatory signature, no degraded mode)
assert publisher._approve(item.id, "gustavo", sess, signature=None) == 2
assert publisher._approve(item.id, "operator", sess, signature=None) == 2
# signed approval accepted, FULL signature stored
sig = _sign_content(item)
assert publisher._approve(item.id, "gustavo", sess, signature=sig) == 0
assert publisher._approve(item.id, "operator", sess, signature=sig) == 0
assert item.status is ContentStatus.APPROVED
assert item.operator_signature == sig and len(sig) == 128
assert publisher._approve(item.id, "gustavo", sess, signature=sig) == 1 # not draft anymore
assert publisher._approve(item.id, "operator", sess, signature=sig) == 1 # not draft anymore
actor = sess.query(AuditEntry).filter_by(action="approve_content").first().actor
assert actor == "gustavo"
assert actor == "operator"
# --- publishing ------------------------------------------------------------------
@@ -183,7 +183,7 @@ def test_foreign_deployment_signature_refused(cfg, sess, signer):
other_dep = signer.raw(
signer.msg("content", "other-deploy", item.id, signer.content_hash(item))
)
assert publisher._approve(item.id, "gustavo", sess, signature=other_dep) == 2
assert publisher._approve(item.id, "operator", sess, signature=other_dep) == 2
assert item.status is ContentStatus.DRAFT