Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 41s
fetch abstracts how a peer's agenda arrives: (fetch peer-id ws we) ->
{:status :ok :occurrences} | {:status :error}. ev/federated-agenda-via merges
local + trusted peers fetched via the transport; unreachable peers degrade
gracefully. ev/peer-fetch = in-process adapter; ev/federation-status reports
reachability. A real fed-sx transport drops in unchanged. 278/278 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
233 lines
7.1 KiB
Plaintext
233 lines
7.1 KiB
Plaintext
;; lib/events/federation.sx — cross-instance calendar federation (trust-gated).
|
|
;;
|
|
;; A peer is another events instance that publishes a schedule (an events
|
|
;; store). We merge a peer's agenda into ours ONLY if we trust it — trust is a
|
|
;; set of peer ids, re-checked on every merge, so revoking a peer takes effect
|
|
;; immediately. Merged occurrences carry :origin provenance (:local for ours, or
|
|
;; the peer id) so a consumer always knows where a slot came from.
|
|
;;
|
|
;; This is the trust-gated stub: peers publish plain schedules and we fold the
|
|
;; trusted ones into a single sorted agenda. Real transport (fed-sx / signed
|
|
;; fetch) slots in behind `ev/peer-agenda` without changing the merge.
|
|
;;
|
|
;; Federated FREE/BUSY follows the iCal model: a peer publishes BUSY intervals
|
|
;; for an actor (not event details — privacy-preserving), and we union local +
|
|
;; trusted-peer busy to answer "is this actor free?" across instances.
|
|
|
|
(define ev/peer (fn (id store) {:id id :busy (list) :store store}))
|
|
|
|
;; A peer that also publishes free/busy: `busy` is a list of
|
|
;; (actor ((start end) ...)) pairs.
|
|
(define ev/peer-with-busy (fn (id store busy) {:id id :busy busy :store store}))
|
|
|
|
(define ev/peer-id (fn (p) (get p :id)))
|
|
(define ev/peer-store (fn (p) (get p :store)))
|
|
(define ev/peer-busy-table (fn (p) (get p :busy)))
|
|
|
|
(define
|
|
ev-fed-member?
|
|
(fn
|
|
(x xs)
|
|
(cond
|
|
((empty? xs) false)
|
|
((= x (first xs)) true)
|
|
(else (ev-fed-member? x (rest xs))))))
|
|
|
|
;; Do we trust this peer id? (trust is a list of trusted peer ids.)
|
|
(define ev/trusts? (fn (trust peer-id) (ev-fed-member? peer-id trust)))
|
|
|
|
;; The trusted subset of a peer list.
|
|
(define
|
|
ev/trusted-peers
|
|
(fn
|
|
(peers trust)
|
|
(filter (fn (p) (ev/trusts? trust (ev/peer-id p))) peers)))
|
|
|
|
;; Tag occurrences with provenance.
|
|
(define ev-tag-origin (fn (occs origin) (map (fn (o) {:id (get o :id) :start (get o :start) :end (get o :end) :origin origin}) occs)))
|
|
|
|
;; A peer's agenda over [ws, we), tagged with the peer's id as :origin.
|
|
(define
|
|
ev/peer-agenda
|
|
(fn
|
|
(peer ws we)
|
|
(ev-tag-origin (ev/agenda (ev/peer-store peer) ws we) (ev/peer-id peer))))
|
|
|
|
;; ---- merge (sorted by start, then origin for ties) ----
|
|
|
|
(define
|
|
ev-fed-before?
|
|
(fn
|
|
(a c)
|
|
(cond
|
|
((< (get a :start) (get c :start)) true)
|
|
((> (get a :start) (get c :start)) false)
|
|
(else (< (str (get a :origin)) (str (get c :origin)))))))
|
|
|
|
(define
|
|
ev-fed-insert
|
|
(fn
|
|
(x sorted)
|
|
(cond
|
|
((empty? sorted) (list x))
|
|
((ev-fed-before? x (first sorted)) (cons x sorted))
|
|
(else (cons (first sorted) (ev-fed-insert x (rest sorted)))))))
|
|
|
|
(define
|
|
ev-fed-sort
|
|
(fn (xs) (reduce (fn (acc x) (ev-fed-insert x acc)) (list) xs)))
|
|
|
|
;; Local agenda (origin :local) merged with every TRUSTED peer's agenda,
|
|
;; sorted by start. Untrusted peers contribute nothing.
|
|
(define
|
|
ev/federated-agenda
|
|
(fn
|
|
(local-store peers trust ws we)
|
|
(let
|
|
((acc (list)))
|
|
(begin
|
|
(for-each
|
|
(fn (o) (append! acc o))
|
|
(ev-tag-origin (ev/agenda local-store ws we) :local))
|
|
(for-each
|
|
(fn
|
|
(peer)
|
|
(when
|
|
(ev/trusts? trust (ev/peer-id peer))
|
|
(for-each
|
|
(fn (o) (append! acc o))
|
|
(ev/peer-agenda peer ws we))))
|
|
peers)
|
|
(ev-fed-sort acc)))))
|
|
|
|
;; Filter a federated agenda to occurrences from one origin.
|
|
(define
|
|
ev/from-origin
|
|
(fn
|
|
(agenda origin)
|
|
(filter (fn (o) (= (get o :origin) origin)) agenda)))
|
|
|
|
;; ---- federated free/busy ----
|
|
|
|
;; A peer's published busy intervals for `actor` ((start end) ...), or empty.
|
|
(define
|
|
ev/peer-busy
|
|
(fn
|
|
(peer actor)
|
|
(let
|
|
((row (ev-fed-assoc actor (ev/peer-busy-table peer))))
|
|
(if (nil? row) (list) (first (rest row))))))
|
|
|
|
(define
|
|
ev-fed-assoc
|
|
(fn
|
|
(k pairs)
|
|
(cond
|
|
((empty? pairs) nil)
|
|
((= (first (first pairs)) k) (first pairs))
|
|
(else (ev-fed-assoc k (rest pairs))))))
|
|
|
|
;; All busy intervals for `actor` across the LOCAL availability db plus every
|
|
;; TRUSTED peer's published free/busy, merged and sorted by start.
|
|
;; `local-db` is an availability db (see availability.sx ev-build-avail).
|
|
(define
|
|
ev/federated-busy
|
|
(fn
|
|
(local-db peers trust actor)
|
|
(let
|
|
((acc (list)))
|
|
(begin
|
|
(for-each (fn (iv) (append! acc iv)) (ev-busy local-db actor))
|
|
(for-each
|
|
(fn
|
|
(peer)
|
|
(when
|
|
(ev/trusts? trust (ev/peer-id peer))
|
|
(for-each
|
|
(fn (iv) (append! acc iv))
|
|
(ev/peer-busy peer actor))))
|
|
peers)
|
|
(ev-sort-lists acc)))))
|
|
|
|
;; Half-open overlap of interval (s e) with window [qs, qe).
|
|
(define
|
|
ev-fed-overlaps?
|
|
(fn (iv qs qe) (and (< (first iv) qe) (< qs (first (rest iv))))))
|
|
|
|
;; Is `actor` free across [qs, qe) considering local + trusted-peer busy?
|
|
(define
|
|
ev/federated-free?
|
|
(fn
|
|
(local-db peers trust actor qs qe)
|
|
(not
|
|
(some
|
|
(fn (iv) (ev-fed-overlaps? iv qs qe))
|
|
(ev/federated-busy local-db peers trust actor)))))
|
|
|
|
;; ---- injected transport (real fed-sx / signed fetch) ----
|
|
;; The in-process merge above expands a peer's local :store directly. In
|
|
;; production a peer's agenda arrives over a transport. `fetch` abstracts that:
|
|
;; (fetch peer-id ws we) -> {:status :ok :occurrences (...)} | {:status :error :reason ...}
|
|
;; The same merge works for any transport; an unreachable peer (:error) is
|
|
;; skipped (graceful degradation), never breaking the agenda.
|
|
|
|
(define
|
|
ev-find-peer
|
|
(fn
|
|
(peers pid)
|
|
(cond
|
|
((empty? peers) nil)
|
|
((= (ev/peer-id (first peers)) pid) (first peers))
|
|
(else (ev-find-peer (rest peers) pid)))))
|
|
|
|
;; In-process transport adapter: resolves a peer-id against a peer list and
|
|
;; expands its :store. Lets the in-process model run through the same `fetch`
|
|
;; interface a remote transport implements.
|
|
(define
|
|
ev/peer-fetch
|
|
(fn
|
|
(peers)
|
|
(fn
|
|
(pid ws we)
|
|
(let
|
|
((p (ev-find-peer peers pid)))
|
|
(if
|
|
(nil? p)
|
|
{:status :error :reason :unknown-peer}
|
|
{:status :ok :occurrences (ev/agenda (ev/peer-store p) ws we)})))))
|
|
|
|
;; Local agenda (:local) merged with each trusted peer's agenda fetched via the
|
|
;; injected `fetch` transport, sorted by start, tagged with :origin. Peers that
|
|
;; fail to fetch contribute nothing.
|
|
(define
|
|
ev/federated-agenda-via
|
|
(fn
|
|
(local-store trusted-ids ws we fetch)
|
|
(let
|
|
((acc (list)))
|
|
(begin
|
|
(for-each
|
|
(fn (o) (append! acc o))
|
|
(ev-tag-origin (ev/agenda local-store ws we) :local))
|
|
(for-each
|
|
(fn
|
|
(pid)
|
|
(let
|
|
((res (fetch pid ws we)))
|
|
(when
|
|
(= (get res :status) :ok)
|
|
(for-each
|
|
(fn (o) (append! acc o))
|
|
(ev-tag-origin (get res :occurrences) pid)))))
|
|
trusted-ids)
|
|
(ev-fed-sort acc)))))
|
|
|
|
;; Reachability report: ((peer-id :ok|:error) ...) for the trusted peers.
|
|
(define
|
|
ev/federation-status
|
|
(fn
|
|
(trusted-ids ws we fetch)
|
|
(map
|
|
(fn (pid) (list pid (get (fetch pid ws we) :status)))
|
|
trusted-ids)))
|