diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index d618aa1..a741552 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -14,7 +14,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-15T00:16:30Z"; + var SX_VERSION = "2026-03-15T00:27:14Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -757,6 +757,12 @@ var charFromCode = PRIMITIVES["char-from-code"]; + // String/number utilities needed by transpiled spec code (content-hash etc) + PRIMITIVES["char-code-at"] = function(s, i) { return s.charCodeAt(i); }; + var charCodeAt = PRIMITIVES["char-code-at"]; + PRIMITIVES["to-hex"] = function(n) { return (n >>> 0).toString(16); }; + var toHex = PRIMITIVES["to-hex"]; + // ========================================================================= // Platform: CEK module — explicit CEK machine // ========================================================================= @@ -5795,10 +5801,6 @@ PRIMITIVES["resource"] = resource; PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; }; PRIMITIVES["make-env"] = function() { return merge(componentEnv, PRIMITIVES); }; - // String/number utilities for content addressing - PRIMITIVES["char-code-at"] = function(s, i) { return s.charCodeAt(i); }; - PRIMITIVES["to-hex"] = function(n) { return (n >>> 0).toString(16); }; - // localStorage — defined here (before boot) so islands can use at hydration PRIMITIVES["local-storage-get"] = function(key) { try { var v = localStorage.getItem(key); return v === null ? NIL : v; } diff --git a/shared/sx/ref/platform_js.py b/shared/sx/ref/platform_js.py index 060baf3..aab6f80 100644 --- a/shared/sx/ref/platform_js.py +++ b/shared/sx/ref/platform_js.py @@ -1481,6 +1481,12 @@ PLATFORM_JS_POST = ''' PLATFORM_CEK_JS = ''' + // String/number utilities needed by transpiled spec code (content-hash etc) + PRIMITIVES["char-code-at"] = function(s, i) { return s.charCodeAt(i); }; + var charCodeAt = PRIMITIVES["char-code-at"]; + PRIMITIVES["to-hex"] = function(n) { return (n >>> 0).toString(16); }; + var toHex = PRIMITIVES["to-hex"]; + // ========================================================================= // Platform: CEK module — explicit CEK machine // ========================================================================= @@ -1532,10 +1538,6 @@ CEK_FIXUPS_JS = ''' PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; }; PRIMITIVES["make-env"] = function() { return merge(componentEnv, PRIMITIVES); }; - // String/number utilities for content addressing - PRIMITIVES["char-code-at"] = function(s, i) { return s.charCodeAt(i); }; - PRIMITIVES["to-hex"] = function(n) { return (n >>> 0).toString(16); }; - // localStorage — defined here (before boot) so islands can use at hydration PRIMITIVES["local-storage-get"] = function(key) { try { var v = localStorage.getItem(key); return v === null ? NIL : v; } diff --git a/sx/sx/geography/cek.sx b/sx/sx/geography/cek.sx index 6616c83..cfb68c4 100644 --- a/sx/sx/geography/cek.sx +++ b/sx/sx/geography/cek.sx @@ -502,7 +502,7 @@ (~cssx/tw :tokens "px-3 py-1 rounded border border-stone-300 font-mono text-sm"))) ;; Live output (div (~cssx/tw :tokens "rounded bg-violet-50 border border-violet-200 p-3 text-violet-800") - (str "Hello, " (deref name) "! Count = " (deref count))) + "Hello, " (deref name) "! Count = " (deref count)) ;; Content address button (div (~cssx/tw :tokens "flex gap-2") (button :on-click (fn (e) @@ -593,7 +593,7 @@ (~geography/cek/freeze-demo)) (~docs/section :title "Content addressing" :id "content-addressing" - (p "Hash the frozen SX " (~docs/inline-code "\u2192") " content identifier. " + (p "Hash the frozen SX " (code "\u2192") " content identifier. " "Same state always produces the same CID. Store by CID, retrieve by CID, verify by CID.") (~docs/code :code (highlight "(freeze-to-cid \"widget\")\n;; => \"d9eea67b\"\n\n(thaw-from-cid \"d9eea67b\")\n;; Signals restored. Same CID = same state." @@ -711,6 +711,63 @@ (deref saved))))))) + + +(defcomp ~geography/cek/cek-content-address-content () + (~docs/page :title "Content-Addressed Computation" + + (p :class "text-stone-500 text-sm italic mb-8" + "A computation is a value. A value has a hash. The hash is the address. " + "Same state, same address, forever.") + + (~docs/section :title "The idea" :id "idea" + (p "Freeze a scope to SX. Hash the SX text. The hash is a content identifier (CID). " + "Store the frozen SX keyed by CID. Later, look up the CID, thaw, resume.") + (p "The critical property: " + (strong "same state always produces the same CID") ". " + "Two machines freezing identical signal values get identical CIDs. " + "The address IS the content.")) + + (~docs/section :title "How it works" :id "how" + (~docs/code :code (highlight + ";; Freeze a scope \u2192 hash \u2192 CID\n(freeze-to-cid \"widget\")\n;; => \"d9eea67b\"\n\n;; The frozen SX is stored by CID\n(content-get \"d9eea67b\")\n;; => {:name \"widget\" :signals {:count 42 :name \"hello\"}}\n\n;; Thaw from CID \u2192 signals restored\n(thaw-from-cid \"d9eea67b\")\n;; Signals reset to frozen values" + "lisp")) + (p "The hash is djb2 for now \u2014 deterministic and fast. " + "Real deployment uses SHA-256 / multihash for IPFS compatibility.")) + + (~docs/section :title "Why it matters" :id "why" + (ul :class "list-disc pl-6 mb-4 space-y-2 text-stone-600" + (li (strong "Sharing") " \u2014 send a CID, not a blob. " + "The receiver looks up the CID and gets the exact state.") + (li (strong "Deduplication") " \u2014 same state = same CID. " + "Store once, reference many times.") + (li (strong "Verification") " \u2014 re-freeze, compare CIDs. " + "If they match, the state is identical. No diffing needed.") + (li (strong "History") " \u2014 each CID is a snapshot. " + "A sequence of CIDs is a complete undo history.") + (li (strong "Distribution") " \u2014 CIDs on IPFS are global. " + "Pin a widget state, anyone can thaw it. " + "No server, no API, no account."))) + + (~docs/section :title "Live demo" :id "demo" + (p "Change the counter and name. Click " (strong "Content-address") " to freeze and hash. " + "The CID appears below. Change the values, then click any CID in the history " + "or paste one into the input to restore.") + (~geography/cek/content-address-demo)) + + (~docs/section :title "The path to IPFS" :id "ipfs" + (p "The content store is currently in-memory. The next steps:") + (ul :class "list-disc pl-6 mb-4 space-y-1 text-stone-600" + (li "Replace djb2 with SHA-256 (browser SubtleCrypto)") + (li "Wrap in multihash + CIDv1 format") + (li "Store to IPFS via the Art DAG L2 registry") + (li "Pin CIDs attributed to " (code "sx-web.org")) + (li "Anyone can pin, fork, extend")) + (p "The frozen SX is the content. The CID is the address. " + "IPFS is the network. The widget state becomes a permanent, " + "verifiable, shareable artifact \u2014 not trapped in a database, " + "not behind an API, not owned by anyone.")))) + ;; --------------------------------------------------------------------------- ;; Demo page content ;; --------------------------------------------------------------------------- diff --git a/sx/sx/nav-data.sx b/sx/sx/nav-data.sx index 3d46dcb..9f22763 100644 --- a/sx/sx/nav-data.sx +++ b/sx/sx/nav-data.sx @@ -176,7 +176,9 @@ (dict :label "Demo" :href "/sx/(geography.(cek.demo))" :summary "Live islands evaluated by the CEK machine. Counter, computed chains, reactive attributes — all through explicit continuation frames.") (dict :label "Freeze / Thaw" :href "/sx/(geography.(cek.freeze))" - :summary "Serialize a CEK state to s-expressions. Ship it, store it, content-address it. Thaw and resume anywhere."))) + :summary "Serialize a CEK state to s-expressions. Ship it, store it, content-address it. Thaw and resume anywhere.") + (dict :label "Content Addressing" :href "/sx/(geography.(cek.content))" + :summary "Hash frozen state to a CID. Same state = same address. Store, share, verify, reproduce."))) (define plans-nav-items (list (dict :label "Status" :href "/sx/(etc.(plan.status))" diff --git a/sx/sx/page-functions.sx b/sx/sx/page-functions.sx index 91794c1..08f3629 100644 --- a/sx/sx/page-functions.sx +++ b/sx/sx/page-functions.sx @@ -67,6 +67,7 @@ (case slug "demo" '(~geography/cek/cek-demo-content) "freeze" '(~geography/cek/cek-freeze-content) + "content" '(~geography/cek/cek-content-address-content) :else '(~geography/cek/cek-content))))) (define provide