diff --git a/README.md b/README.md index 64d1f86..a7e7f29 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/tests/integration/test_pg_roles.py b/tests/integration/test_pg_roles.py index 398ca91..309dbd2 100644 --- a/tests/integration/test_pg_roles.py +++ b/tests/integration/test_pg_roles.py @@ -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}" ) diff --git a/tests/test_executor.py b/tests/test_executor.py index 5c8f382..65e4a46 100644 --- a/tests/test_executor.py +++ b/tests/test_executor.py @@ -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 diff --git a/tests/test_maild.py b/tests/test_maild.py index d317073..05d9cb0 100644 --- a/tests/test_maild.py +++ b/tests/test_maild.py @@ -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 ) diff --git a/tests/test_models.py b/tests/test_models.py index 41f7828..cee5783 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -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 diff --git a/tests/test_publisher.py b/tests/test_publisher.py index d0d79f3..605068b 100644 --- a/tests/test_publisher.py +++ b/tests/test_publisher.py @@ -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