diff --git a/docs/sexpr-unified-protocol.md b/docs/sexpr-unified-protocol.md index 36879f3..fb79379 100644 --- a/docs/sexpr-unified-protocol.md +++ b/docs/sexpr-unified-protocol.md @@ -108,26 +108,77 @@ Every expression on the wire is one of these forms: #### Requests (client → server) +The head symbol is the verb. **Any symbol is a valid verb.** There is no fixed set like HTTP's seven methods. The verb describes the intent; the path is the noun: + ```scheme -(GET "/markets/" - :auth "bearer tok_abc123" - :have "sha3-a1b2c3" - :have-components ("sha3-aaa" "sha3-bbb")) +;; Reads +(GET "/markets/") +(GET "/markets/" :have "sha3-a1b2c3" :have-components ("sha3-aaa" "sha3-bbb")) +(GET "/feed/" :stream #t) -(POST "/like/" - :auth "bearer tok_abc123" - :body (:post-id 123)) +;; Writes (HTTP-style, but you're not limited to these) +(POST "/users/" :body (:name "alice" :email "alice@example.com")) +(DELETE "/posts/draft-123/") -(POST "/submit/" +;; Domain verbs — describe what's actually happening +(publish "/posts/draft-123/") +(reserve "/events/saturday-market/" :tickets 2) +(subscribe "/newsletters/weekly/") +(unsubscribe "/newsletters/weekly/") +(pin "/posts/important-announcement/") +(refund "/orders/ord-789/" :reason "damaged") +(schedule "/calendar/saturday/" :slot 3 :name "Pottery Workshop") +(rsvp "/events/annual-meeting/" :attending #t :guests 2) +(transfer "/inventory/sourdough/" :from "stall-a" :to "stall-b" :quantity 5) +(bid "/auctions/vintage-table/" :amount 45.00) + +;; Cooperative governance +(propose "/governance/" :title "New market hours" + :body (p "I suggest we open at 8am...")) +(second "/governance/proposals/42/") +(vote "/governance/proposals/42/" :choice :approve) +(ratify "/governance/proposals/42/") + +;; Compute mesh +(render :recipe "bafyrecipe..." :input "bafyinput..." :requirements (:min-vram 8)) +(transcode :input "bafyvideo..." :format "h265" :quality 28) + +;; File uploads — structured, not multipart MIME +(POST "/upload/" :body ( :username "alice" - :email "alice@example.com" :avatar (file :name "photo.jpg" :type "image/jpeg" :data ))) - -;; Streaming request — keep connection open for pushes -(GET "/feed/" :stream #t) ``` +`(reserve "/events/saturday-market/" :tickets 2)` reads as English. An AI agent doesn't need API documentation to understand what that does. The URL is the noun, the verb is the verb — no more `POST /api/users/123/send-password-reset-email` where the action is buried in the URL because HTTP doesn't have the right verb. + +**Verb behaviour is declared in the schema**, not assumed by convention: + +```scheme +(GET "/__schema/") + +(ok + (schema + (verb GET :idempotent #t :auth :optional + :description "Retrieve a resource") + (verb reserve :idempotent #f :auth :required + :params (:tickets int) + :returns (reservation) + :description "Reserve tickets for an event") + (verb cancel-reservation :idempotent #t :auth :required + :params (:reservation-id str) + :returns (ok) + :description "Cancel a ticket reservation") + (verb publish :idempotent #t :auth :admin + :description "Publish a draft post") + (verb vote :idempotent #t :auth :member + :params (:choice (enum :approve :reject :abstain)) + :returns (ok) + :description "Vote on a governance proposal"))) +``` + +No more arguing about whether `PATCH` should be idempotent. The schema says what each verb does, what it takes, what it returns, and who can call it. AI agents read the schema and know the full API surface — including domain-specific verbs they've never seen before. + #### Responses (server → client) ```scheme