From 95df738bdc1e8c59682f990b6cd1a0b019a2e3a3 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 28 Mar 2026 12:20:29 +0000 Subject: [PATCH] SXTP spec: SX Transfer Protocol for the native browser Full protocol specification covering requests, responses, verbs, headers, cookies, status conditions, addressing, streaming, capabilities, caching, and wire format. Everything is s-expressions. Co-Authored-By: Claude Opus 4.6 (1M context) --- applications/sxtp/spec.sx | 155 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 applications/sxtp/spec.sx diff --git a/applications/sxtp/spec.sx b/applications/sxtp/spec.sx new file mode 100644 index 00000000..afa8938e --- /dev/null +++ b/applications/sxtp/spec.sx @@ -0,0 +1,155 @@ +(define + request-fields + (quote + ((:verb "Symbol — the action to perform (required)") + (:path "String — resource path (required)") + (:headers "Dict — structured request metadata (optional)") + (:cookies "Dict — client state, values can be any SX type (optional)") + (:params "Dict — query parameters as typed values (optional)") + (:capabilities "List — capabilities this request requires (optional)") + (:body "Any SX value — request payload (optional)")))) + +(define + response-fields + (quote + ((:status "Symbol or condition — result status (required)") + (:headers "Dict — structured response metadata (optional)") + (:set-cookie + "Dict — cookies to set, values are dicts with :value :max-age :path (optional)") + (:body "Any SX value — response payload (optional)") + (:stream "Boolean — if true, body is a sequence of chunks (optional)")))) + +(define + core-verbs + (quote + ((navigate "Retrieve a page for display — analogous to GET for documents") + (fetch "Retrieve data — analogous to GET for APIs") + (query "Structured query — body contains a query expression") + (mutate "Change state — analogous to POST/PUT/PATCH") + (create "Create a new resource — analogous to POST") + (delete "Remove a resource — analogous to DELETE") + (subscribe "Open a streaming channel for real-time updates") + (inspect "Retrieve metadata about a resource (capabilities, schema)") + (ping "Liveness check — server responds with (response :status ok)")))) + +(define + standard-headers + (quote + ((:accept "List of acceptable response types") + (:language "String or list — preferred languages") + (:if-match "String — content hash for conditional requests") + (:capabilities "List — capabilities the client holds") + (:origin "String — requesting origin for CORS-like checks") + (:content-type "String — always text/sx in pure SXTP") + (:content-hash "String — SHA3-256 of the body expression") + (:cache "Symbol — :immutable, :revalidate, :none") + (:vary "List of header keys that affect caching") + (:link "Dict — related resources")))) + +(define + cookie-options + (quote + ((:value "Any SX value — the cookie payload (required)") + (:max-age "Number — seconds until expiry (optional)") + (:path "String — path scope (optional, default /)") + (:domain "String — domain scope (optional)") + (:secure "Boolean — require secure transport (optional)") + (:same-site "Symbol — :strict, :lax, or :none (optional)") + (:delete "Boolean — if true, remove this cookie (optional)")))) + +(define + status-symbols + (quote + ((ok "Success — body contains the result") + (created "Resource created — body contains the new resource") + (accepted "Request accepted for async processing") + (no-content "Success with no body") + (redirect "See :headers :location for target") + (not-modified "Cached version is current based on :if-match") + (error "General error — see :body for condition") + (not-found "Resource does not exist") + (forbidden "Insufficient capabilities") + (invalid "Malformed request or invalid params") + (conflict "State conflict — concurrent edit") + (unavailable "Service temporarily unavailable")))) + +(define + condition-fields + (quote + ((:type "Symbol — condition type (required)") + (:message "String — human-readable description (optional)") + (:path "String — resource that caused the error (optional)") + (:retry "Boolean — whether retrying may succeed (optional)") + (:detail "Any SX value — domain-specific detail (optional)")))) + +(define + chunk-fields + (quote + ((:seq "Number — sequence index for ordered chunks") + (:body "Any SX value — the chunk content") + (:done "Boolean — signals end of stream")))) + +(define + event-fields + (quote + ((:type "Symbol — event type (required)") + (:id "String — event or resource identifier (optional)") + (:body "Any SX value — event payload (optional)") + (:time "Number — unix timestamp (optional)")))) + +(define + example-navigate + (quote + ((request :verb navigate :path "/geography/capabilities" :headers {:host "sx.rose-ash.com" :accept "text/sx"}) + (response + :status ok + :headers {:content-type "text/sx" :content-hash "sha3-9f2a"} + :body (page + :title "Capabilities" + (h1 "Geography Capabilities") + (~capability-list :domain "geography")))))) + +(define + example-query + (quote + ((request :verb query :path "/events" :capabilities (fetch db:read) :params {:after "2026-03-01" :limit 10} :body (filter (events) (fn (e) (> (:attendees e) 50)))) + (response + :status ok + :headers {:cache :revalidate} + :body ((event :id "evt-42" :title "Jazz Night" :attendees 87) + (event :id "evt-55" :title "Art Walk" :attendees 120)))))) + +(define + example-mutate + (quote + ((request :verb create :path "/blog/posts" :capabilities (mutate blog:publish) :cookies {:session "tok_abc123"} :body {:tags ("protocol" "sx" "web") :body (article (h1 "SXTP") (p "Everything is SX.")) :title "SXTP Protocol"}) + (response :status created :headers {:location "/blog/posts/sxtp-protocol" :content-hash "sha3-ff01"} :body {:created-at 1711612800 :id "post-789" :path "/blog/posts/sxtp-protocol"})))) + +(define + example-subscribe + (quote + ((request :verb subscribe :path "/events/live" :capabilities (fetch) :headers {:host "events.rose-ash.com"}) + (response :status ok :stream true) + (event + :type new-event + :id "evt-99" + :body (div :class "event-card" (h3 "Poetry Slam"))) + (event :type heartbeat :time 1711612860)))) + +(define + example-error + (quote + ((request :verb fetch :path "/blog/nonexistent") + (response + :status not-found + :body (condition + :type resource-not-found + :path "/blog/nonexistent" + :message "No such post" + :retry false))))) + +(define + example-inspect + (quote + ((request :verb inspect :path "/cart/checkout") + (response :status ok :body {:available-verbs (inspect mutate) :params-schema {:payment-method "symbol" :shipping-address "dict"} :required-capabilities (mutate cart:checkout)}))))