journal

An append-only, hash-chained, tenant-partitioned event log and the deterministic projection of that log into state. Open or attach a journal over a `store` backend, `append` attributed events (each returning a committed `[entry]` with its sequence, content hash, and predecessor link), `read` / `slice` / `query` ranges, and `fold` the log into a state projection. The chain is deterministic — replayable, dry-runnable, and hash-verifiable — which is the entire reason it exists. `snapshot`, `retain`, and `compact` accelerate and bound replay as derived artifacts; they never edit the live chain, so append-only and chain integrity hold permanently.

journal:open

[$journal:open] -> element — Open a journal over a `store` backend URL for one tenant, creating or attaching to its hash chain.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://acme" "acme"]]
  [$journal:append $j [do :refund] {actor: "ops" authority: "d-77"}]]
          
            [entry seq=1 tenant=acme actor=ops authority=d-77 ts='epoch:00:00:01' prev-hash=b3:GENESIS hash=sha256:bdc6c60e64d8bc43a17fc1762633a915d8816ca2dffd429b45ce19dda82d6178 [event [do :refund]]]
          

journal:attach

[$journal:attach] -> element — Attach a journal to an already-open `store` handle for one tenant, reusing its connection.

            [?lib 'cx-stdlib/store']
[?lib 'cx-stdlib/journal']
[?let [= $s [$store:open "mem://r"]]
[= $j1 [$journal:attach $s "acme"]]
[= $a [$journal:append $j1 [a 1] {actor: "u" authority: "g"}]]
[= $b [$journal:append $j1 [b 2] {actor: "u" authority: "g"}]]
[= $c [$journal:close $j1]]
[= $j2 [$journal:attach $s "acme"]]
  [$journal:verify $j2]]
          
            [verification valid=true checked-from=1 checked-to=2 head-hash='sha256:2bcb8230b14039e30cc935fb9af79aa4047d317167b0e6edb95e674a82b7e8c2']
          

journal:close

[$journal:close] -> null — Close the journal handle; subsequent operations on it raise CXER4612.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $x [$journal:close $j]]
  [$journal:append $j [a] {actor: "u" authority: "g"}]]
          
            cx-err:CXER4612
          

journal:append

[$journal:append] -> element — Commit an attributed event, returning the new `[entry]` with its seq, content hash, and predecessor link.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://acme" "acme"]]
  [$journal:append $j [do :refund] {actor: "ops" authority: "d-77"}]]
          
            [entry seq=1 tenant=acme actor=ops authority=d-77 ts='epoch:00:00:01' prev-hash=b3:GENESIS hash=sha256:bdc6c60e64d8bc43a17fc1762633a915d8816ca2dffd429b45ce19dda82d6178 [event [do :refund]]]
          

journal:read

[$journal:read] -> element — Return the single `[entry]` at `seq`, or the absence channel if out of range.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a 1] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b 2] {actor: "u" authority: "g"}]]
  [$journal:read $j 2]]
          
            [entry seq=2 tenant=acme actor=u authority=g ts='epoch:00:00:02' prev-hash=sha256:f0d42b6c75733de803ac02bb66eecf005add595cadca2f7151f1c682ecb440d0 hash='sha256:2bcb8230b14039e30cc935fb9af79aa4047d317167b0e6edb95e674a82b7e8c2' [event [b 2]]]
          

journal:slice

[$journal:slice] -> [sequence element] — Return the entries with `from ≤ seq ≤ to` (inclusive, 1-based), in sequence order.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $e3 [$journal:append $j [c] {actor: "u" authority: "g"}]]
  [$journal:slice $j 2 3]]
          
            ([entry seq=2 tenant=acme actor=u authority=g ts='epoch:00:00:02' prev-hash='sha256:117af1f326b2d1ee4d4b56b66b3fa13dabfbc55045e4cc57740c7c5f026f163d' hash='sha256:9cd1fca3841e0ea3f6db09fc032c901cd1f9b84a7089ebb90c82753c06ef56aa' [event [b]]], [entry seq=3 tenant=acme actor=u authority=g ts='epoch:00:00:03' prev-hash='sha256:9cd1fca3841e0ea3f6db09fc032c901cd1f9b84a7089ebb90c82753c06ef56aa' hash='sha256:6c29ae866f4b35ead2bc4de4bb388382d1c4542ec46828c568a0d63f60117c43' [event [c]]])
          

journal:since

[$journal:since] -> [sequence element] — Return the tail of the log from `from` to the current head.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
  [$journal:since $j 2]]
          
            ([entry seq=2 tenant=acme actor=u authority=g ts='epoch:00:00:02' prev-hash='sha256:117af1f326b2d1ee4d4b56b66b3fa13dabfbc55045e4cc57740c7c5f026f163d' hash='sha256:9cd1fca3841e0ea3f6db09fc032c901cd1f9b84a7089ebb90c82753c06ef56aa' [event [b]]])
          

journal:query

[$journal:query] -> [sequence element] — Return the entries whose `[event …]` payload or envelope matches a CXPath filter.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [do :refund] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [note "hi"] {actor: "u" authority: "g"}]]
  [$journal:query $j "/event/do"]]
          
            ([entry seq=1 tenant=acme actor=u authority=g ts='epoch:00:00:01' prev-hash=b3:GENESIS hash=sha256:fcd34ab5565c2823273d24922fe5e7b53d3ffe02fd62695a99b214d409933c30 [event [do :refund]]])
          

journal:head

[$journal:head] -> element — Return the current head `[entry]`, or absence if the log is empty (pure — reads the cached head).

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
  [$journal:head $j]]
          
            ()
          

journal:streams

[$journal:streams] -> [sequence element] — List the distinct stream names present in the journal.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $a [$journal:append $j [x 1] {actor: "u" authority: "g" stream: "s1"}]]
[= $b [$journal:append $j [x 2] {actor: "u" authority: "g" stream: "s1"}]]
[= $c [$journal:append $j [y 1] {actor: "u" authority: "g" stream: "s2"}]]
  [$journal:streams $j]]
          
            ('s1', 's2')
          

journal:fold

[$journal:fold] -> any — Fold the whole log into a state projection with a pure reducer and initial value.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $e3 [$journal:append $j [c] {actor: "u" authority: "g"}]]
  [$journal:fold $j [?fn ($a $e) [+ $a 1]] 0]]
          
            3
          

journal:fold-slice

[$journal:fold-slice] -> any — Fold a `from..to` range of the log into a state projection.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $e3 [$journal:append $j [c] {actor: "u" authority: "g"}]]
  [$journal:fold-slice $j [?fn ($a $e) [+ $a 1]] 0 2 3]]
          
            2
          

journal:fold-value

[$journal:fold-value] -> any — Fold an already-read entry sequence into state, without touching the journal.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $es [$journal:since $j 1]]
  [$journal:fold-value $es [?fn ($a $e) [+ $a 10]] 0]]
          
            20
          

journal:replay

[$journal:replay] -> any — Deterministically re-execute the log into state, honouring replay options such as `at-seq`.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $e3 [$journal:append $j [c] {actor: "u" authority: "g"}]]
  [$journal:replay $j [?fn ($a $e) [+ $a 1]] 0 {at-seq: 2}]]
          
            2
          

journal:dry-run

[$journal:dry-run] -> element — Preview the state and `[entry]` an append would produce, without committing it.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
  [$journal:dry-run $j [b] {actor: "u" authority: "g"} [?fn ($a $e) [+ $a 1]] 0]]
          
            [dry-run [state 2] [entry seq=2 tenant=acme actor=u authority=g ts='epoch:00:00:02' prev-hash='sha256:117af1f326b2d1ee4d4b56b66b3fa13dabfbc55045e4cc57740c7c5f026f163d' hash='sha256:9cd1fca3841e0ea3f6db09fc032c901cd1f9b84a7089ebb90c82753c06ef56aa' [event [b]]]]
          

journal:verify

[$journal:verify] -> element — Verify hash-chain integrity end to end, returning a `[verification]` finding (not an error on corruption).

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
  [$journal:verify $j {}]]
          
            [verification valid=true checked-from=1 checked-to=2 head-hash='sha256:9cd1fca3841e0ea3f6db09fc032c901cd1f9b84a7089ebb90c82753c06ef56aa']
          

journal:verify-slice

[$journal:verify-slice] -> element — Verify hash-chain integrity over a `from..to` range.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $e3 [$journal:append $j [c] {actor: "u" authority: "g"}]]
  [$journal:verify-slice $j 2 3]]
          
            [verification valid=true checked-from=2 checked-to=3 head-hash='sha256:6c29ae866f4b35ead2bc4de4bb388382d1c4542ec46828c568a0d63f60117c43']
          

journal:snapshot

[$journal:snapshot] -> element — Fold the log up to a sequence into a signed, derived state checkpoint so fold need not re-run from genesis.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
  [$journal:snapshot $j [?fn ($a $e) [+ $a 1]] 0 {sign: "false"}]]
          
            [snapshot tenant=acme at-seq=1 anchor-hash='sha256:117af1f326b2d1ee4d4b56b66b3fa13dabfbc55045e4cc57740c7c5f026f163d' hash-algo=sha256 sig-algo=none [state 1]]
          

journal:snapshot-verify

[$journal:snapshot-verify] -> element — Check a `[snapshot]` against the chain and against its own signature.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $bad [snapshot tenant=acme at-seq=1 anchor-hash="sha256:deadbeef" hash-algo=sha256 sig-algo=none [state 99]]]
  [$journal:snapshot-verify $j $bad]]
          
            [snapshot-verification valid=false at-seq=1 reason=':anchor-mismatch']
          

journal:fold-from

[$journal:fold-from] -> any — Reconstruct state fast: verify a snapshot, then fold only the tail of entries after it.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $e3 [$journal:append $j [c] {actor: "u" authority: "g"}]]
[= $snap [$journal:snapshot $j [?fn ($a $e) [+ $a 1]] 0 {sign: "false" at-seq: 1}]]
[= $full [$journal:fold $j [?fn ($a $e) [+ $a 1]] 0]]
[= $fast [$journal:fold-from $j $snap [?fn ($a $e) [+ $a 1]]]]
  [equiv full=$full fast=$fast]]
          
            [equiv full=3 fast=3]
          

journal:retain

[$journal:retain] -> element — Record a prune-behind-a-snapshot `[retention]` policy and check its snapshot coverage; deletes nothing.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://t" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $snap [$journal:snapshot $j [?fn ($a $e) [+ $a 1]] 0 {signing-key: "0101010101010101010101010101010101010101010101010101010101010101" at-seq: 2}]]
  [$journal:retain $j {keep-after-seq: 2 snapshot: $snap}]]
          
            [retention boundary=2 covered-by-seq=2]
          

journal:compact

[$journal:compact] -> element — Copy a snapshot plus the retained tail forward into a new segment, leaving the source chain intact.

            [?lib 'cx-stdlib/journal']
[?let [= $j [$journal:open "mem://src" "acme"]]
[= $e1 [$journal:append $j [a] {actor: "u" authority: "g"}]]
[= $e2 [$journal:append $j [b] {actor: "u" authority: "g"}]]
[= $e3 [$journal:append $j [c] {actor: "u" authority: "g"}]]
[= $snap [$journal:snapshot $j [?fn ($a $e) [+ $a 1]] 0 {signing-key: "0101010101010101010101010101010101010101010101010101010101010101" at-seq: 2}]]
[= $ret [$journal:retain $j {keep-after-seq: 2 snapshot: $snap}]]
[= $seg [$journal:compact $j {retention: $ret target: "mem://seg"}]]
  [$journal:verify $j {}]]
          
            [verification valid=true checked-from=1 checked-to=3 head-hash='sha256:6c29ae866f4b35ead2bc4de4bb388382d1c4542ec46828c568a0d63f60117c43']