;; lib/mod/audit.sx — append-only decision log. ;; ;; Every decision the api commits is recorded as an immutable audit entry holding ;; the decision (action + matching rule), the proof tree (the derivation that ;; justified it), and a snapshot of the evidence in force at decision time. The ;; log is append-only: entries are never mutated or removed, only appended, each ;; with a monotonic sequence number. Retrieval is by report id (full history) or ;; by sequence. (define mod/*audit-log* (list)) (define mod/*audit-seq* 0) (define mod/audit-reset! (fn () (begin (set! mod/*audit-log* (list)) (set! mod/*audit-seq* 0)))) (define mod/mk-audit-entry (fn (seq decision evidence-snapshot) {:action (get decision :action) :evidence evidence-snapshot :proof (get decision :proof) :rule (get decision :rule) :report-id (get decision :report-id) :seq seq})) (define mod/log-decision! (fn (decision evidence-snapshot) (begin (set! mod/*audit-seq* (+ mod/*audit-seq* 1)) (let ((entry (mod/mk-audit-entry mod/*audit-seq* decision evidence-snapshot))) (begin (append! mod/*audit-log* entry) entry))))) ;; entries for one report, in chronological (sequence) order (define mod/audit (fn (id) (reduce (fn (acc e) (if (= (get e :report-id) id) (append acc (list e)) acc)) (list) mod/*audit-log*))) (define mod/audit-all (fn () mod/*audit-log*)) (define mod/audit-count (fn () (len mod/*audit-log*))) ;; most recent decision logged for a report (nil if none) (define mod/audit-latest (fn (id) (reduce (fn (acc e) (if (= (get e :report-id) id) e acc)) nil mod/*audit-log*)))