fabric

Platform-level eventing over the shipped primitives: one subscribe/emit surface with an explicit durability axis. The durable plane rides journal — publish appends an attributed event to a named stream (per-stream total order, hash-chained), subscribe/receive is a batched pull filtered by the bus pattern vocabulary, and ack commits a consumer group's offset cumulatively as store data, giving at-least-once delivery with redelivery of the uncommitted tail; a group may carry a redelivery policy (max-deliveries + dlq) that dead-letters a poison head-of-tail event to an ordinary stream and unblocks the group. The transient plane is latest-wins in-process channels — no history, no replay, no ack — and carries the request-reply call convention: respond registers an arity-1 callable answering a channel (sticky-exclusive), request is a blocking deadline-bounded call, serve pumps a remote responder. fabric introduces no capability of its own; persistence authority is the underlying journal/store's.

fabric:open

[$fabric:open] -> element — Open a fabric, returning a [fabric] handle — dual-form: a [journal] element composes the embedded tier over it (the journal stays caller-owned); an xsp://host:port URL string dials a fabric-serve daemon and attaches via XSP-AUTH (opts.tenant required; opts.did+seed = the proven identity, absent = anonymous; net-gated at the dial).

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  $f@state]
          
            'open'
          

fabric:close

[$fabric:close] -> null — Close the fabric: cancel subscriptions, drop transient channels — idempotent; the journal is untouched.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $c [$fabric:close $f]]
  [$fabric:read $f "acme/coord/map"]]
          
            cx-err:CXER4921
          

fabric:publish

[$fabric:publish] -> element — Append an attributed event to a named durable stream, returning a [receipt seq=N stream=…] — the journal append is the sequencing.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $r [$fabric:publish $f "orders" [do :order.placed] {actor: "u" authority: "g"}]]
  $r@seq]
          
            1
          

fabric:subscribe

[$fabric:subscribe] -> element — Register a pull subscription on a durable stream — pattern-filtered; with opts.group it resumes from the group's committed offset. Group subscriptions may declare a redelivery policy (opts.max-deliveries + opts.dlq, together or not at all): the policy persists as group state, later group subscriptions inherit it, a conflicting redeclaration refuses, and a head-of-tail event redelivered past the limit is published to the dlq stream as a [dead-letter] envelope and auto-committed past — the group unblocks, the original entry stays in the source journal.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $r [$fabric:publish $f "orders" [do :order.placed] {actor: "u" authority: "g"}]]
  [= $s [$fabric:subscribe $f "orders" :order.*]]
  [$count [$fabric:receive $s]]]
          
            1
          

fabric:observe

[$fabric:observe] -> element — Register a read-only subscription — receive works, ack refuses (the wire-tap shape; no offsets, no group).

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $r [$fabric:publish $f "orders" [do :order.placed] {actor: "u" authority: "g"}]]
  [= $s [$fabric:observe $f "orders" :order.placed]]
  [$fabric:ack $s 1]]
          
            cx-err:CXER4922
          

fabric:receive

[$fabric:receive] -> [sequence element] — Batched pull: return the pattern-matching [entry] elements committed since the cursor (up to opts.max), advancing the cursor — never blocks; empty node-set when nothing.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $r [$fabric:publish $f "orders" [do :order.placed] {actor: "u" authority: "g"}]]
  [= $s [$fabric:subscribe $f "orders" "do"]]
  [$count [$fabric:receive $s]]]
          
            1
          

fabric:ack

[$fabric:ack] -> null — Commit the group's offset cumulatively through seq, persisted as store data — a later group subscription resumes past it; the uncommitted tail redelivers.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $r [$fabric:publish $f "orders" [do :order.placed] {actor: "u" authority: "g"}]]
  [= $s1 [$fabric:subscribe $f "orders" :order.placed {group: "g1"}]]
  [= $e1 [$fabric:receive $s1]]
  [= $a [$fabric:ack $s1 1]]
  [= $s2 [$fabric:subscribe $f "orders" :order.placed {group: "g1"}]]
  [$count [$fabric:receive $s2]]]
          
            0
          

fabric:emit

[$fabric:emit] -> null — Publish a value on a transient channel — latest-wins, no history, no replay, out of the durable audit.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $e1 [$fabric:emit $f "acme/coord/map" [viewport zoom=12]]]
  [= $e2 [$fabric:emit $f "acme/coord/map" [viewport zoom=13]]]
  [$fabric:read $f "acme/coord/map"]]
          
            [viewport zoom=13]
          

fabric:read

[$fabric:read] -> any — Read a transient channel's latest value — the empty node-set when the channel was never published (absence, never null).

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $e [$fabric:emit $f "acme/coord/map" [viewport zoom=12]]]
  [$fabric:read $f "acme/coord/map"]]
          
            [viewport zoom=12]
          

fabric:respond

[$fabric:respond] -> element — Register an arity-1 callable answering a request-reply channel, returning a [fabric-responder …] — sticky-exclusive per channel (a second respond while the holder lives refuses); the callable never travels: on the remote tier only the registration crosses the wire and the serve pump applies it here.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $r [$fabric:respond $f "acme/svc/echo" [?fn ($v) $v]]]
  $r@channel]
          
            'acme/svc/echo'
          

fabric:request

[$fabric:request] -> any — Blocking request-reply call on a channel: the reply is the responder callable's return value; a responder err (raised or returned) travels verbatim; no live responder refuses immediately; opts.deadline bounds the remote wait (ms, default 10000). Embedded requests are answered synchronously at the call site.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $r [$fabric:respond $f "acme/svc/sum" [?fn ($v) [+ $v/a $v/b]]]]
  [$fabric:request $f "acme/svc/sum" [args [a 2] [b 3]]]]
          
            5
          

fabric:serve

[$fabric:serve] -> int — The remote responder's pump (pull is the primitive): drain pushed request frames, apply the registered callable, send the replies — returns the count of calls answered (opts: deadline ms, max). Embedded calls are answered at the request site, so the embedded pump reports 0 by construction.

            [?lib 'cx-stdlib/journal']
[?lib 'cx-fabric' :as fabric]
[?let [= $j [$journal:open "mem://t" "acme"]]
  [= $f [$fabric:open $j]]
  [= $r [$fabric:respond $f "acme/svc/echo" [?fn ($v) $v]]]
  [$fabric:serve $r]]
          
            0