Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 40s
explain.sx reconstructs a canonical proof tree (first-rule, first-solution)
by goal-directed search over the saturated db, since Datalog keeps no
provenance; depth-capped for cyclic safety. acl-explain returns
{:allowed? :proof :reason} with the blocking eff_deny proof on denial.
audit.sx is an append-only decision log (monotonic seq, disk serializer).
api gains acl/explain, acl/audit, acl/audit-tail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
83 lines
2.1 KiB
Plaintext
83 lines
2.1 KiB
Plaintext
;; lib/acl/audit.sx — append-only decision log.
|
|
;;
|
|
;; Every decision routed through acl-audit-decide! is appended to an in-memory
|
|
;; log with a monotonic sequence number (no wall-clock — deterministic and
|
|
;; testable; a host can stamp time at the serializer boundary). The log is
|
|
;; append-only: there is no mutate or delete, only append, tail, clear, and
|
|
;; serialize-for-disk.
|
|
|
|
(define acl-audit-log (list))
|
|
(define acl-audit-seq 0)
|
|
|
|
(define
|
|
acl-audit-clear!
|
|
(fn
|
|
()
|
|
(do (set! acl-audit-log (list)) (set! acl-audit-seq 0) nil)))
|
|
|
|
;; Append a decision record. Returns the record.
|
|
(define
|
|
acl-audit-record!
|
|
(fn
|
|
(subj act res allowed?)
|
|
(let
|
|
((entry {:allowed? allowed? :act act :subj subj :res res :seq acl-audit-seq}))
|
|
(do
|
|
(set! acl-audit-seq (+ acl-audit-seq 1))
|
|
(append! acl-audit-log entry)
|
|
entry))))
|
|
|
|
;; Decide against db, log the outcome, and return the boolean. This is the
|
|
;; audited path; acl-permit? remains the pure, side-effect-free decision.
|
|
(define
|
|
acl-audit-decide!
|
|
(fn
|
|
(db subj act res)
|
|
(let
|
|
((allowed? (acl-permit? db subj act res)))
|
|
(do (acl-audit-record! subj act res allowed?) allowed?))))
|
|
|
|
(define acl-audit-count (fn () (len acl-audit-log)))
|
|
|
|
;; Most recent n entries (in chronological order). n >= log size returns all.
|
|
(define
|
|
acl-audit-tail
|
|
(fn
|
|
(n)
|
|
(let
|
|
((total (len acl-audit-log)))
|
|
(if
|
|
(<= total n)
|
|
acl-audit-log
|
|
(acl-audit-drop acl-audit-log (- total n))))))
|
|
|
|
(define
|
|
acl-audit-drop
|
|
(fn
|
|
(xs k)
|
|
(if (<= k 0) xs (acl-audit-drop (rest xs) (- k 1)))))
|
|
|
|
;; Serialize the whole log to a disk-ready string: one record per line,
|
|
;; "seq\tsubj\tact\tres\tallowed?". A host writes this; reload is out of scope.
|
|
(define
|
|
acl-audit-serialize
|
|
(fn
|
|
()
|
|
(reduce
|
|
(fn
|
|
(acc e)
|
|
(str
|
|
acc
|
|
(get e :seq)
|
|
"\t"
|
|
(get e :subj)
|
|
"\t"
|
|
(get e :act)
|
|
"\t"
|
|
(get e :res)
|
|
"\t"
|
|
(get e :allowed?)
|
|
"\n"))
|
|
""
|
|
acl-audit-log)))
|