audit-4: serialized audit chain + legacy genesis + daily Nostr anchoring
- audit() writes are serialized: pg_advisory_xact_lock on Postgres; in-process RLock held until session commit/rollback on SQLite (test: 4 concurrent writers x 25 entries -> chain verifies, 100 rows) - legacy rows (entry_hash NULL, pre-0002) are skipped: the chain begins GENESIS at the first hashed row; migrated DBs verify clean by design - monitor anchor: publishes the last chain hash as a signed NIP-01 note once per day, logs it in the audit log (self-verified before publish) tests: concurrent writers; legacy-genesis verification; anchor signs, logs once per day.
This commit is contained in:
@@ -67,3 +67,42 @@ def test_build_escapes_injected_content(cfg, sess, tmp_path):
|
||||
page = out.read_text()
|
||||
assert "<script>x</script>" not in page
|
||||
assert "<script>x</script>" in page
|
||||
|
||||
|
||||
def test_anchor_signs_and_logs_once_per_day(cfg, sess, monkeypatch):
|
||||
from cayceops.components.monitor import monitor
|
||||
from cayceops.models import AuditEntry
|
||||
|
||||
# seed the chain
|
||||
from cayceops.models import audit as audit_fn
|
||||
|
||||
audit_fn(sess, actor="daemon:executor", action="execute_done", detail={})
|
||||
sess.flush()
|
||||
|
||||
published = []
|
||||
|
||||
def fake_publish(relays, event, timeout=10.0):
|
||||
published.append(event)
|
||||
return [{"relay": r, "ok": "true"} for r in relays]
|
||||
|
||||
import cayceops.components.publisher.nostr as nostr_mod
|
||||
|
||||
monkeypatch.setattr(nostr_mod, "publish", fake_publish)
|
||||
|
||||
config = cfg(
|
||||
CAYCE_OPS_NOSTR_PRIVATE_KEY="11" * 32,
|
||||
CAYCE_OPS_NOSTR_RELAYS="wss://r.example.org",
|
||||
CAYCE_OPS_DEPLOYMENT_ID="itest",
|
||||
)
|
||||
code = monitor._anchor(config, sess)
|
||||
assert code == 0
|
||||
assert len(published) == 1
|
||||
content = published[0]["content"]
|
||||
assert "audit anchor" in content and "itest" in content
|
||||
entry = sess.query(AuditEntry).filter_by(action="anchor").one()
|
||||
assert entry.detail["last_hash"]
|
||||
|
||||
# second call same day -> skipped, no new nostr event
|
||||
code2 = monitor._anchor(config, sess)
|
||||
assert code2 == 0
|
||||
assert len(published) == 1
|
||||
|
||||
Reference in New Issue
Block a user