sx-pub Phase 4: anchoring — Merkle trees, OpenTimestamps, verification

New endpoints:
- POST /pub/anchor — batch unanchored Publish activities into Merkle tree,
  pin tree to IPFS, submit root to OpenTimestamps, store OTS proof on IPFS
- GET /pub/verify/<cid> — verify a CID's Merkle proof against anchored tree

Uses existing shared/utils/anchoring.py infrastructure:
- build_merkle_tree (SHA256, deterministic sort)
- get_merkle_proof / verify_merkle_proof (inclusion proofs)
- submit_to_opentimestamps (3 calendar servers with fallback)

Tested: anchored 1 activity, Merkle tree + OTS proof pinned to IPFS,
verification returns :verified true with full proof chain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 02:12:40 +00:00
parent aa1d4d7a67
commit d12f38a9d5
3 changed files with 230 additions and 0 deletions

View File

@@ -337,3 +337,59 @@
" :actor-url \"" (get f "actor-url") "\")"))
data)))
(str "(SxFollowing" (join "" items) ")")))))
;; ==========================================================================
;; Phase 4: Anchoring — Merkle trees, OTS, verification
;; ==========================================================================
;; --------------------------------------------------------------------------
;; Anchor pending activities
;; --------------------------------------------------------------------------
(defhandler pub-anchor
:path "/pub/anchor"
:method :post
:csrf false
:returns "element"
(&key)
(let ((result (helper "pub-anchor-pending")))
(do
(set-response-header "Content-Type" "text/sx; charset=utf-8")
(if (= (get result "status") "nothing-to-anchor")
"(Anchor :status \"nothing-to-anchor\" :count 0)"
(str
"(Anchor"
"\n :status \"" (get result "status") "\""
"\n :count " (get result "count")
"\n :merkle-root \"" (get result "merkle-root") "\""
"\n :tree-cid \"" (get result "tree-cid") "\""
"\n :ots-proof-cid \"" (get result "ots-proof-cid") "\")")))))
;; --------------------------------------------------------------------------
;; Verify a CID's anchor
;; --------------------------------------------------------------------------
(defhandler pub-verify
:path "/pub/verify/<cid>"
:method :get
:returns "element"
(&key cid)
(let ((data (helper "pub-verify-anchor" cid)))
(do
(set-response-header "Content-Type" "text/sx; charset=utf-8")
(if (get data "error")
(do
(set-response-status 404)
(str "(Error :message \"" (get data "error") "\")"))
(str
"(AnchorVerification"
"\n :cid \"" (get data "cid") "\""
"\n :status \"" (get data "status") "\""
"\n :verified " (get data "verified")
"\n :merkle-root \"" (get data "merkle-root") "\""
"\n :tree-cid \"" (get data "tree-cid") "\""
"\n :ots-proof-cid \"" (get data "ots-proof-cid") "\""
"\n :published \"" (get data "published") "\")")))))