; persist/blob — large objects (images, media) are NOT persist's to hold. They ; live in a content-addressed store (artdag/IPFS); persist stores only a ; reference: {:cid :size :mime}. The blob store is a SEPARATE injected ; dependency with its own transport (perform in production, a mock content store ; in tests), distinct from the event/kv backend. The invariant: a blob ref that ; lands in the log or kv carries the CID + metadata and never the bytes. ; Requires: lib/persist/backend.sx. (define persist/blob-ref (fn (cid size mime) {:mime mime :size size :cid cid})) (define persist/blob-ref? (fn (r) (has-key? r :cid))) (define persist/blob-cid (fn (r) (get r :cid))) (define persist/blob-size (fn (r) (get r :size))) (define persist/blob-mime (fn (r) (get r :mime))) ; blob store protocol over an injectable transport (define persist/blob-io (fn (transport) {:put (fn (bytes mime) (transport {:op "blob/put" :args (list bytes mime)})) :get (fn (cid) (transport {:op "blob/get" :args (list cid)})) :has? (fn (cid) (transport {:op "blob/has?" :args (list cid)}))})) ; production blob store — transport is the kernel's perform (define persist/blob-store-backend (fn () (persist/blob-io (fn (req) (perform req))))) ; store bytes via the blob backend; return ONLY the ref (cid + metadata) — this ; is what the caller persists in the log/kv. The bytes never enter persist. (define persist/blob-store (fn (blob bytes mime) (let ((cid ((get blob :put) bytes mime))) (persist/blob-ref cid (len bytes) mime)))) (define persist/blob-fetch (fn (blob ref) ((get blob :get) (persist/blob-cid ref)))) (define persist/blob-exists? (fn (blob ref) ((get blob :has?) (persist/blob-cid ref)))) ; mock content-addressed store (stands in for artdag/IPFS). CID is a ; deterministic content address: identical bytes dedupe to one CID. A real ; store computes a SHA3/IPFS CID host-side; the prefix keeps the mock readable. (define persist/blob-cid-of (fn (bytes) (str "cid:" bytes))) (define persist/blob-serve (fn (store req) (let ((op (get req :op)) (args (get req :args))) (cond ((equal? op "blob/put") (let ((cid (persist/blob-cid-of (first args)))) (begin (persist/backend-kv-put store cid (first args)) cid))) ((equal? op "blob/get") (persist/backend-kv-get store (first args))) ((equal? op "blob/has?") (persist/backend-kv-has? store (first args))) (else (error (str "persist/blob-serve: unknown op " op))))))) (define persist/blob-mock-transport (fn (store) (fn (req) (persist/blob-serve store req)))) (define persist/mock-blob (fn (store) (persist/blob-io (persist/blob-mock-transport store))))