Files
rose-ash/sx/sx/plans/environment-images.sx
giles 6f96452f70 Fix empty code blocks: rename ~docs/code param, fix batched IO dispatch
Two bugs caused code blocks to render empty across the site:

1. ~docs/code component had parameter named `code` which collided with
   the HTML <code> tag name. Renamed to `src` and updated all 57
   callers. Added font-mono class for explicit monospace.

2. Batched IO dispatch in ocaml_bridge.py only skipped one leading
   number (batch ID) but the format has two (epoch + ID):
   (io-request EPOCH ID "name" args...). Changed to skip all leading
   numbers so the string name is correctly found. This fixes highlight
   and other batchable helpers returning empty results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 18:08:40 +00:00

305 lines
26 KiB
Plaintext

;; ---------------------------------------------------------------------------
;; Content-Addressed Environment Images
;; ---------------------------------------------------------------------------
(defcomp ~plans/environment-images/plan-environment-images-content ()
(~docs/page :title "Content-Addressed Environment Images"
(~docs/section :title "The Idea" :id "idea"
(p "Every served SX endpoint should point back to its spec. The spec CIDs identify the exact evaluator, renderer, parser, and primitives that produced the output. This makes every endpoint " (strong "fully executable") " — anyone with the CIDs can independently reproduce the result.")
(p "But evaluating spec files from source on every cold start is wasteful. The specs are pure — same source always produces the same evaluated environment. So we can serialize the " (em "evaluated") " environment as a content-addressed image: all defcomps, defmacros, bound symbols, resolved closures frozen into a single artifact. The image CID is a function of its contents. Load the image, skip evaluation, get the same result.")
(p "The chain becomes:")
(ol :class "list-decimal pl-5 text-stone-700 space-y-2"
(li (strong "Served page") " → CID of the spec that defines its semantics")
(li (strong "Spec CID") " → the evaluator, renderer, parser, primitives that any conforming host can execute")
(li (strong "Image CID") " → the pre-evaluated environment, a cache of (2) that any conforming host can deserialize"))
(p "The spec is the truth. The image is a verified cache. The bootstrapper that compiled the spec for a particular host is an implementation detail — irrelevant to the content address.")
(div :class "rounded border border-violet-200 bg-violet-50 p-4 mt-4"
(p :class "text-violet-900 font-medium" "Prior art")
(p :class "text-violet-800" (a :href "https://github.com/KinaKnowledge/juno-lang" :class "underline" "Juno") " — a self-hosted Lisp-to-JS compiler — implements image persistence: serialize a running environment, restore it later, even bundle it as a standalone HTML document. Their Seedling IDE saves/restores entire development sessions as images. SX can do this more rigorously because our images are content-addressed (Juno's are not) and our components are boundary-enforced pure (Juno's are not).")))
;; -----------------------------------------------------------------------
;; What gets serialized
;; -----------------------------------------------------------------------
(~docs/section :title "What Gets Serialized" :id "what"
(p "An environment image is a snapshot of everything produced by evaluating the spec files. Not the source — the result.")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Category")
(th :class "px-3 py-2 font-medium text-stone-600" "Contents")
(th :class "px-3 py-2 font-medium text-stone-600" "Source")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Components")
(td :class "px-3 py-2 text-stone-700" "All " (code "defcomp") " definitions — name, params, body AST, closure bindings, CID, deps, css_classes")
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "Service .sx files + shared/sx/templates/"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Macros")
(td :class "px-3 py-2 text-stone-700" "All " (code "defmacro") " definitions — name, params, body AST, closure")
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "Spec files"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Bindings")
(td :class "px-3 py-2 text-stone-700" "Top-level " (code "define") " values — constants, lookup tables, configuration")
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "Spec files + service .sx"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Primitives")
(td :class "px-3 py-2 text-stone-700" "Registry of pure primitive names (not implementations — those are host-specific)")
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "primitives.sx, boundary.sx"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Spec provenance")
(td :class "px-3 py-2 text-stone-700" "CIDs of the spec files that produced this image")
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "eval.sx, render.sx, parser.sx, ...")))))
(p "Notably " (strong "absent") " from the image:")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li (strong "IO primitive implementations") " — these are host-specific. The image records their " (em "names") " (for boundary enforcement) but not their code.")
(li (strong "Page helpers") " — same reason. " (code "fetch-data") ", " (code "app-url") " etc. are registered by the host app at startup.")
(li (strong "Runtime state") " — no request context, no DB connections, no session data. The image is a pure function's result, not a running process snapshot.")))
;; -----------------------------------------------------------------------
;; Image format
;; -----------------------------------------------------------------------
(~docs/section :title "Image Format" :id "format"
(p "The image is itself an s-expression — the same format the spec is written in. This means the image can be parsed by the same parser, inspected by the same tools, and content-addressed by the same canonical serializer.")
(~docs/code :src (highlight "(sx-image\n :version 1\n :spec-cids {:eval \"bafy...eval\"\n :render \"bafy...render\"\n :parser \"bafy...parser\"\n :primitives \"bafy...prims\"\n :boundary \"bafy...boundary\"\n :signals \"bafy...signals\"}\n\n :components (\n (defcomp ~plans/environment-images/card (&key title subtitle &rest children)\n (div :class \"card\" (h2 title) (when subtitle (p subtitle)) children))\n (defcomp ~plans/environment-images/nav (&key items current)\n (nav :class \"nav\" (map (fn (item) ...) items)))\n ;; ... all registered components\n )\n\n :macros (\n (defmacro when (test &rest body)\n (list 'if test (cons 'begin body) nil))\n ;; ... all macros\n )\n\n :bindings (\n (define void-elements (list \"area\" \"base\" \"br\" \"col\" ...))\n (define boolean-attrs (list \"checked\" \"disabled\" ...))\n ;; ... all top-level defines\n )\n\n :primitive-names (\"str\" \"+\" \"-\" \"*\" \"/\" \"=\" \"<\" \">\" ...)\n :io-names (\"fetch-data\" \"call-action\" \"app-url\" ...))" "lisp"))
(p "The " (code ":spec-cids") " field is the key. It links this image back to the exact spec that produced it. Anyone can verify the image by:")
(ol :class "list-decimal pl-5 text-stone-700 space-y-1"
(li "Fetch the spec files by CID")
(li "Evaluate them with a conforming evaluator")
(li "Serialize the resulting environment")
(li "Compare — it must produce the same image")))
;; -----------------------------------------------------------------------
;; Image CID
;; -----------------------------------------------------------------------
(~docs/section :title "Image CID" :id "image-cid"
(p "The image CID is computed by canonical-serializing the entire " (code "(sx-image ...)") " form and hashing it. Same process as component CIDs, just applied to the whole environment.")
(p "The relationship between spec CIDs and image CID is deterministic:")
(~docs/code :src (highlight ";; The image CID is a pure function of the spec CIDs\n;; (assuming a deterministic evaluator, which SX guarantees)\n(define image-cid-from-specs\n (fn (spec-cids)\n ;; 1. Fetch each spec file by CID\n ;; 2. Evaluate all specs in a fresh environment\n ;; 3. Extract components, macros, bindings\n ;; 4. Build (sx-image ...) form\n ;; 5. Canonical serialize\n ;; 6. Hash → CID\n ))" "lisp"))
(p "This means you can compute the expected image CID from the spec CIDs " (em "without") " having the image. If someone hands you an image claiming to be from spec " (code "bafy...eval") ", you can verify it by re-evaluating the spec and comparing CIDs. The image is a verifiable cache.")
(p "In practice, you'd only do this verification once per spec version. After that, the image CID is trusted by content-addressing — same bytes, same hash, forever."))
;; -----------------------------------------------------------------------
;; Endpoint provenance
;; -----------------------------------------------------------------------
(~docs/section :title "Endpoint Provenance" :id "provenance"
(p "Every served page gains a provenance header linking it to the spec that rendered it:")
(~docs/code :src (highlight "HTTP/1.1 200 OK\nContent-Type: text/html\nSX-Spec: bafy...eval,bafy...render,bafy...parser,bafy...prims\nSX-Image: bafy...image\nSX-Page-Components: ~plans/environment-images/card:bafy...card,~plans/environment-images/nav:bafy...nav" "http"))
(p "Three levels of verification:")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Level")
(th :class "px-3 py-2 font-medium text-stone-600" "What you verify")
(th :class "px-3 py-2 font-medium text-stone-600" "Trust assumption")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-semibold text-stone-800" "Component")
(td :class "px-3 py-2 text-stone-700" "Fetch " (code "~plans/environment-images/card") " by CID, verify hash")
(td :class "px-3 py-2 text-stone-600" "Trust the evaluator"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-semibold text-stone-800" "Image")
(td :class "px-3 py-2 text-stone-700" "Fetch image by CID, deserialize, re-render page")
(td :class "px-3 py-2 text-stone-600" "Trust the image producer"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-semibold text-stone-800" "Spec")
(td :class "px-3 py-2 text-stone-700" "Fetch specs by CID, re-evaluate, compare image CID")
(td :class "px-3 py-2 text-stone-600" "Trust only the hash function")))))
(p "Level 3 is the nuclear option — full independent verification from source. It's expensive but proves the entire chain. Most consumers will operate at level 1 (component verification) or level 2 (image verification)."))
;; -----------------------------------------------------------------------
;; Cold start optimization
;; -----------------------------------------------------------------------
(~docs/section :title "Cold Start: Images as Cache" :id "cold-start"
(p "The practical motivation: evaluating all spec files + service components on every server restart is slow. An image eliminates this.")
(~docs/subsection :title "Server Startup"
(ol :class "list-decimal pl-5 text-stone-700 space-y-2"
(li "Check if a cached image exists for the current spec CIDs")
(li "If yes: deserialize the image (fast — parsing a single file, no evaluation)")
(li "If no: evaluate spec files from source, build image, cache it")
(li "Register IO primitives and page helpers (host-specific, not in image)")
(li "Ready to serve"))
(~docs/code :src (highlight "(define load-environment\n (fn (spec-cids image-cache-dir)\n (let ((expected-image-cid (image-cid-for-specs spec-cids))\n (cached-path (str image-cache-dir \"/\" expected-image-cid \".sx\")))\n (if (file-exists? cached-path)\n ;; Fast path: deserialize\n (let ((image (parse (read-file cached-path))))\n (if (= (verify-image-cid image) expected-image-cid)\n (deserialize-image image)\n ;; Cache corrupted — rebuild\n (build-and-cache-image spec-cids image-cache-dir)))\n ;; Cold path: evaluate from source\n (build-and-cache-image spec-cids image-cache-dir)))))" "lisp")))
(~docs/subsection :title "Client Boot"
(p "The client already caches component definitions in localStorage keyed by bundle hash. Images extend this: cache the entire evaluated environment, not just individual components.")
(ol :class "list-decimal pl-5 text-stone-700 space-y-1"
(li "Page ships " (code "SX-Image") " header with image CID")
(li "Client checks localStorage for " (code "sx-image:{cid}"))
(li "If hit: deserialize and boot (no component-by-component parsing)")
(li "If miss: fetch image from origin or IPFS, deserialize, cache")
(li "Same cache-forever semantics as component CIDs — content can't be stale"))
(p "First visit to any SX-powered site: one image fetch. Every subsequent visit: instant boot from cache. Cross-site: if two sites share the same spec CIDs, the image is shared too.")))
;; -----------------------------------------------------------------------
;; Standalone HTML
;; -----------------------------------------------------------------------
(~docs/section :title "Standalone HTML Bundles" :id "standalone"
(p "An image can be inlined into a single HTML document, producing a fully self-contained application with no server dependency:")
(~docs/code :src (highlight "<!doctype html>\n<html>\n<head>\n <script type=\"text/sx-image\">\n (sx-image\n :version 1\n :spec-cids {...}\n :components (...)\n :macros (...)\n :bindings (...))\n </script>\n <script type=\"text/sx-pages\">\n (defpage home :path \"/\" :content (~home-page))\n </script>\n <script src=\"sx-ref.js\"></script>\n</head>\n<body>\n <div id=\"app\"></div>\n <script>\n // Deserialize image, register components, render\n Sx.bootFromImage(document.querySelector('[type=\"text/sx-image\"]'))\n </script>\n</body>\n</html>" "html"))
(p "This document is its own CID. Pin it to IPFS and it's a permanent, executable, verifiable application. No origin server, no CDN, no DNS. The content network is the deployment target.")
(div :class "rounded border border-amber-200 bg-amber-50 p-4 mt-4"
(p :class "text-amber-900 font-medium" "Juno comparison")
(p :class "text-amber-800" "Juno's Seedling IDE already does this — export a running environment as a standalone HTML file. But their images are opaque JavaScript blobs serialized by the runtime. SX images are " (strong "s-expressions") " — parseable, inspectable, content-addressable. You can diff two SX images and see exactly what changed. You can extract a single component from an image by CID. You can merge images from different sources by composing their component lists. The format IS the tooling.")))
;; -----------------------------------------------------------------------
;; Namespace scoping
;; -----------------------------------------------------------------------
(~docs/section :title "Namespaced Environments" :id "namespaces"
(p "As the component library grows across services, a flat environment risks name collisions. Images provide a natural boundary for namespace scoping.")
(~docs/code :src (highlight "(sx-image\n :version 1\n :namespace \"market\"\n :spec-cids {...}\n :extends \"bafy...shared-image\" ;; inherits shared components\n :components (\n ;; market-specific components\n (defcomp ~plans/environment-images/product-card ...)\n (defcomp ~plans/environment-images/price-tag ...)\n ))" "lisp"))
(p "Resolution: " (code "market/~plans/environment-images/product-card") " → look in market image first, then fall through to the shared image (via " (code ":extends") "). Each service produces its own image, layered on top of the shared base.")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Image")
(th :class "px-3 py-2 font-medium text-stone-600" "Contents")
(th :class "px-3 py-2 font-medium text-stone-600" "Extends")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "bafy...shared")
(td :class "px-3 py-2 text-stone-700" "~plans/environment-images/card, ~plans/environment-images/nav, ~nav-data/section-nav, ~docs/page, ~docs/code — shared components from " (code "shared/sx/templates/"))
(td :class "px-3 py-2 text-stone-600" "None (root)"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "bafy...blog")
(td :class "px-3 py-2 text-stone-700" "~shared:cards/post-card, ~post-body, ~tag-list — blog-specific from " (code "blog/sx/"))
(td :class "px-3 py-2 text-stone-600" "bafy...shared"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "bafy...market")
(td :class "px-3 py-2 text-stone-700" "~plans/environment-images/product-card, ~plans/environment-images/price-tag, ~shared:fragments/cart-mini — market-specific from " (code "market/sx/"))
(td :class "px-3 py-2 text-stone-600" "bafy...shared"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "bafy...sx-docs")
(td :class "px-3 py-2 text-stone-700" "~docs/section, ~examples/source, plans, essays — sx docs from " (code "sx/sx/"))
(td :class "px-3 py-2 text-stone-600" "bafy...shared")))))
(p "The " (code ":extends") " field is a CID, not a name. Image composition is content-addressed: changing the shared image produces a new shared CID, which invalidates all service images that extend it. Exactly the right cascading behavior."))
;; -----------------------------------------------------------------------
;; Spec → Image → Page chain
;; -----------------------------------------------------------------------
(~docs/section :title "The Verification Chain" :id "chain"
(p "The full provenance chain from served page back to source:")
(~docs/code :src (highlight ";; 1. Page served with provenance headers\n;;\n;; SX-Spec: bafy...eval,bafy...render,...\n;; SX-Image: bafy...market-image\n;; SX-Page: (defpage product :path \"/products/<slug>\" ...)\n;;\n;; 2. Verify image → spec\n;;\n;; Fetch specs by CID → evaluate → build image → compare CID\n;; If match: the image was correctly produced from these specs\n;;\n;; 3. Verify page → image\n;;\n;; Deserialize image → evaluate page defn → render\n;; If output matches served HTML: the page was correctly rendered\n;;\n;; 4. Trust chain terminates at the spec\n;;\n;; The spec is self-hosting (eval.sx evaluates itself)\n;; The spec's CID is its identity\n;; No external trust anchor needed beyond the hash function" "lisp"))
(p "This is stronger than code signing. Code signing says " (em "\"this entity vouches for this binary.\"") " Content addressing says " (em "\"this binary is the deterministic output of this source.\"") " No entity needed. No certificate authority. No revocation lists. Just math."))
;; -----------------------------------------------------------------------
;; Implementation phases
;; -----------------------------------------------------------------------
(~docs/section :title "Implementation" :id "implementation"
(~docs/subsection :title "Phase 1: Image Serialization"
(p "Spec module " (code "image.sx") " — serialize and deserialize evaluated environments.")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li (code "serialize-environment") " — walk the env, extract components/macros/bindings, produce " (code "(sx-image ...)") " form")
(li (code "deserialize-image") " — parse image, reconstitute components/macros/bindings into env")
(li (code "image-cid") " — canonical-serialize the image form, hash → CID")
(li "Must handle closure serialization — component closures reference other components by name, which must be re-linked on deserialization")))
(~docs/subsection :title "Phase 2: Spec Provenance"
(p "Compute CIDs for all spec files at startup. Attach to environment metadata.")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li "Hash each spec file's canonical source at load time")
(li "Store in env metadata as " (code ":spec-cids") " dict")
(li "Include in image serialization")))
(~docs/subsection :title "Phase 3: Server-Side Caching"
(p "Cache images on disk keyed by spec CIDs. Skip evaluation on warm restart.")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li "On startup: compute spec CIDs → derive expected image CID → check cache")
(li "Cache hit: deserialize (parse only, no eval)")
(li "Cache miss: evaluate specs, serialize image, write cache")
(li "Any spec file change → new spec CID → new image CID → cache miss → rebuild")))
(~docs/subsection :title "Phase 4: Client Images"
(p "Ship image CID in response headers. Client caches full env in localStorage.")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li (code "SX-Image") " response header with image CID")
(li "Client boot checks localStorage for cached image")
(li "Cache hit: deserialize, skip per-component fetch/parse")
(li "Cache miss: fetch image (single request), deserialize, cache")))
(~docs/subsection :title "Phase 5: Standalone Export"
(p "Generate self-contained HTML with inlined image. Pin to IPFS.")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li "Inline " (code "(sx-image ...)") " as " (code "<script type=\"text/sx-image\">"))
(li "Inline page definitions as " (code "<script type=\"text/sx-pages\">"))
(li "Include sx-ref.js (or link to its CID)")
(li "The resulting HTML is a complete application — pin its CID to IPFS")))
(~docs/subsection :title "Phase 6: Namespaced Images"
(p "Per-service images with " (code ":extends") " for layered composition.")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li "Shared image: components from " (code "shared/sx/templates/"))
(li "Service images: extend shared, add service-specific components")
(li "Resolution: service image → shared image → primitives")
(li "Image merge: combine two images with conflict detection"))))
;; -----------------------------------------------------------------------
;; Dependencies
;; -----------------------------------------------------------------------
(~docs/section :title "Dependencies" :id "dependencies"
(p "What must exist before this plan can execute:")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Dependency")
(th :class "px-3 py-2 font-medium text-stone-600" "Status")
(th :class "px-3 py-2 font-medium text-stone-600" "Plan")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Canonical serialization")
(td :class "px-3 py-2 text-stone-700" (span :class "text-red-700 font-medium" "Not started"))
(td :class "px-3 py-2" (a :href "/sx/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 1"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Component CIDs")
(td :class "px-3 py-2 text-stone-700" (span :class "text-red-700 font-medium" "Not started"))
(td :class "px-3 py-2" (a :href "/sx/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 2"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Purity verification")
(td :class "px-3 py-2 text-stone-700" (span :class "text-green-700 font-medium" "Complete"))
(td :class "px-3 py-2 text-stone-600" "deps.sx + boundary.py"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Self-hosting spec")
(td :class "px-3 py-2 text-stone-700" (span :class "text-green-700 font-medium" "Complete"))
(td :class "px-3 py-2 text-stone-600" "eval.sx, render.sx, parser.sx, ..."))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Self-hosting bootstrappers")
(td :class "px-3 py-2 text-stone-700" (span :class "text-green-700 font-medium" "Complete"))
(td :class "px-3 py-2 text-stone-600" "py.sx, js.sx — G0 == G1"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "IPFS infrastructure")
(td :class "px-3 py-2 text-stone-700" (span :class "text-green-700 font-medium" "Exists"))
(td :class "px-3 py-2 text-stone-600" "artdag L1/L2, IPFSPin model")))))
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
(p :class "text-amber-800 text-sm" (strong "Builds on: ") (a :href "/sx/(etc.(plan.content-addressed-components))" :class "underline" "Content-Addressed Components") " (canonical serialization + CIDs), " (a :href "/sx/(etc.(plan.self-hosting-bootstrapper))" :class "underline" "self-hosting bootstrappers") " (spec-first architecture). " (strong "Enables: ") (a :href "/sx/(etc.(plan.sx-activity))" :class "underline" "SX-Activity") " (serverless applications on IPFS).")))))