Rename all 1,169 components to path-based names with namespace support
Component names now reflect filesystem location using / as path separator and : as namespace separator for shared components: ~sx-header → ~layouts/header ~layout-app-body → ~shared:layout/app-body ~blog-admin-dashboard → ~admin/dashboard 209 files, 4,941 replacements across all services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
;; Content-Addressed Environment Images
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~plan-environment-images-content ()
|
||||
(~doc-page :title "Content-Addressed Environment Images"
|
||||
(defcomp ~plans/environment-images/plan-environment-images-content ()
|
||||
(~docs/page :title "Content-Addressed Environment Images"
|
||||
|
||||
(~doc-section :title "The Idea" :id "idea"
|
||||
(~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:")
|
||||
@@ -22,7 +22,7 @@
|
||||
;; What gets serialized
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "What Gets Serialized" :id "what"
|
||||
(~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"
|
||||
@@ -63,10 +63,10 @@
|
||||
;; Image format
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Image Format" :id "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.")
|
||||
|
||||
(~doc-code :code (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 ~card (&key title subtitle &rest children)\n (div :class \"card\" (h2 title) (when subtitle (p subtitle)) children))\n (defcomp ~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"))
|
||||
(~docs/code :code (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"
|
||||
@@ -79,11 +79,11 @@
|
||||
;; Image CID
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Image CID" :id "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:")
|
||||
|
||||
(~doc-code :code (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"))
|
||||
(~docs/code :code (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."))
|
||||
@@ -92,10 +92,10 @@
|
||||
;; Endpoint provenance
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Endpoint Provenance" :id "provenance"
|
||||
(~docs/section :title "Endpoint Provenance" :id "provenance"
|
||||
(p "Every served page gains a provenance header linking it to the spec that rendered it:")
|
||||
|
||||
(~doc-code :code (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: ~card:bafy...card,~nav:bafy...nav" "http"))
|
||||
(~docs/code :code (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"
|
||||
@@ -107,7 +107,7 @@
|
||||
(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 "~card") " by CID, verify hash")
|
||||
(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")
|
||||
@@ -124,19 +124,19 @@
|
||||
;; Cold start optimization
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Cold Start: Images as Cache" :id "cold-start"
|
||||
(~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.")
|
||||
|
||||
(~doc-subsection :title "Server Startup"
|
||||
(~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"))
|
||||
(~doc-code :code (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/code :code (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")))
|
||||
|
||||
(~doc-subsection :title "Client Boot"
|
||||
(~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")
|
||||
@@ -150,10 +150,10 @@
|
||||
;; Standalone HTML
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Standalone HTML Bundles" :id "standalone"
|
||||
(~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:")
|
||||
|
||||
(~doc-code :code (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"))
|
||||
(~docs/code :code (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.")
|
||||
|
||||
@@ -165,12 +165,12 @@
|
||||
;; Namespace scoping
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Namespaced Environments" :id "namespaces"
|
||||
(~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.")
|
||||
|
||||
(~doc-code :code (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 ~product-card ...)\n (defcomp ~price-tag ...)\n ))" "lisp"))
|
||||
(~docs/code :code (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/~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.")
|
||||
(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"
|
||||
@@ -181,19 +181,19 @@
|
||||
(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" "~card, ~nav, ~section-nav, ~doc-page, ~doc-code — shared components from " (code "shared/sx/templates/"))
|
||||
(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" "~post-card, ~post-body, ~tag-list — blog-specific from " (code "blog/sx/"))
|
||||
(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" "~product-card, ~price-tag, ~cart-mini — market-specific from " (code "market/sx/"))
|
||||
(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" "~doc-section, ~example-source, plans, essays — sx docs from " (code "sx/sx/"))
|
||||
(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."))
|
||||
@@ -202,10 +202,10 @@
|
||||
;; Spec → Image → Page chain
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "The Verification Chain" :id "chain"
|
||||
(~docs/section :title "The Verification Chain" :id "chain"
|
||||
(p "The full provenance chain from served page back to source:")
|
||||
|
||||
(~doc-code :code (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"))
|
||||
(~docs/code :code (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."))
|
||||
|
||||
@@ -213,9 +213,9 @@
|
||||
;; Implementation phases
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Implementation" :id "implementation"
|
||||
(~docs/section :title "Implementation" :id "implementation"
|
||||
|
||||
(~doc-subsection :title "Phase 1: Image Serialization"
|
||||
(~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")
|
||||
@@ -223,14 +223,14 @@
|
||||
(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")))
|
||||
|
||||
(~doc-subsection :title "Phase 2: Spec Provenance"
|
||||
(~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")))
|
||||
|
||||
(~doc-subsection :title "Phase 3: Server-Side Caching"
|
||||
(~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")
|
||||
@@ -238,7 +238,7 @@
|
||||
(li "Cache miss: evaluate specs, serialize image, write cache")
|
||||
(li "Any spec file change → new spec CID → new image CID → cache miss → rebuild")))
|
||||
|
||||
(~doc-subsection :title "Phase 4: Client Images"
|
||||
(~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")
|
||||
@@ -246,7 +246,7 @@
|
||||
(li "Cache hit: deserialize, skip per-component fetch/parse")
|
||||
(li "Cache miss: fetch image (single request), deserialize, cache")))
|
||||
|
||||
(~doc-subsection :title "Phase 5: Standalone Export"
|
||||
(~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\">"))
|
||||
@@ -254,7 +254,7 @@
|
||||
(li "Include sx-ref.js (or link to its CID)")
|
||||
(li "The resulting HTML is a complete application — pin its CID to IPFS")))
|
||||
|
||||
(~doc-subsection :title "Phase 6: Namespaced Images"
|
||||
(~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/"))
|
||||
@@ -266,7 +266,7 @@
|
||||
;; Dependencies
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Dependencies" :id "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"
|
||||
|
||||
Reference in New Issue
Block a user