A WebSub hub: it takes subscriptions, checks each with its subscriber, and delivers each new version of a topic to the topic's subscribers.
Nothing runs in the background. The Ring handler answers subscribers and publishers at once, and gives the work that follows to the :schedule! function of the options as a job, a map that run-job! does. A delivery that fails gives its retry to :schedule! in the same way, with the instant to run it as its :at. The subscriptions are in a store of yours, a map of two functions, or in memory-store.
The code follows sections 5 to 8 of the W3C Recommendation of 2 June 2026, https://www.w3.org/TR/websub/.
A WebSub hub: it takes subscriptions, checks each with its subscriber, and delivers each new version of a topic to the topic's subscribers. Nothing runs in the background. The Ring handler answers subscribers and publishers at once, and gives the work that follows to the :schedule! function of the options as a job, a map that run-job! does. A delivery that fails gives its retry to :schedule! in the same way, with the instant to run it as its :at. The subscriptions are in a store of yours, a map of two functions, or in memory-store. The code follows sections 5 to 8 of the W3C Recommendation of 2 June 2026, https://www.w3.org/TR/websub/.
(active? subscription at)Whether the subscription is active at the instant at, before its
lease ends.
Whether the `subscription` is active at the instant `at`, before its lease ends.
The default of each option of a hub that has one, which follows WebSub:
The default of each option of a hub that has one, which follows WebSub: - :lease-seconds, the lease of a subscription that asks for none, ten days, as WebSub 8.2 suggests - :min-lease-seconds and :max-lease-seconds, the bounds of a lease, an hour and ten days - :retries, the seconds to wait before each new attempt at a delivery that failed: a minute, then 5, 30, 120 and 720 - :signature-method, sha256, as WebSub 8.3 advises - :diff?, whether a subscriber gets only the entries of an Atom or RSS feed that it hasn't had, false - :accept-pred, a function of a job and the request that gave it, which says whether the hub takes it, always true - :secret-pred, a predicate of the secret of a subscription, which takes a text of fewer than 200 bytes, as WebSub 5.1 says - :url-fn, a function of the callback and topic URLs of a subscription, and the topic URLs of a publisher, normalized - :topic-fields, the fields that name the topics of a publisher, in the order that they count: hub.url and hub.topic - :challenge-fn, a function that gives the challenge of each check, websub/token - :verified-pred, a function of the challenge and the subscriber's response, which says whether the check passed, verified? - :callback-redirects, how many redirects the requests to a callback follow, none, as WebSub 5.3.1 and 8.3 say - :delete-gone?, whether a 410 from a subscriber deletes its subscription, which WebSub 7 allows, true - :delete-expired?, whether a subscription is deleted once its lease ends, true - :skip-stale?, whether a retry is left out once the subscriber has had newer content, true - :max-form-bytes, the limit of a request to the hub, 64 kB - :max-bytes, the limit of a topic's content, 32 MB - :concurrency, how many deliveries of a topic are under way at once, 16, so that a topic with thousands of subscribers doesn't open thousands of connections - :allow-pred, url/public?, so that no request goes to the hub's own network - :send, http/send! - :now-fn, a function that gives the instant now
(deliver! {:keys [topic callback content attempt] :as job} opts)Deliver the :content of the job to the subscriber of its :topic and
:callback again, by opts, after a failed :attempt. It gives the
report of the delivery, as distribute! has them.
Deliver the :content of the `job` to the subscriber of its :topic and :callback again, by `opts`, after a failed :attempt. It gives the report of the delivery, as distribute! has them.
(denial job)(denial {:keys [topic callback] :as job} reason)The request map that tells the subscriber of the job that the hub
denies its subscription, for the reason, which can be left out.
The request map that tells the subscriber of the `job` that the hub denies its subscription, for the `reason`, which can be left out.
(deny! {:keys [topic callback reason] :as job} opts)Delete the subscription of the topic and callback of the job, by
opts, and tell the subscriber that the hub denies it for the :reason
of the job. It gives the job with the subscriber's :status, or the
:error.
Delete the subscription of the topic and callback of the `job`, by `opts`, and tell the subscriber that the hub denies it for the :reason of the job. It gives the job with the subscriber's :status, or the :error.
(distribute! {:keys [topic content attempt] :or {attempt 0} :as job} opts)Deliver the new content of the topic of the job to each of its
subscribers, by opts.
The content is the job's :content, a map of the :body bytes and the :content-type, or else what the topic gives when the hub fetches it. Each delivery gives a report of its :outcome: :delivered, :gone for a subscription that the subscriber deleted, :failed with the :retry-at of its retry, or :expired or :stale when there was nothing to deliver. It gives the job with the :deliveries, or with the :error of a fetch, and then the fetch's retry goes to :schedule!.
Deliver the new content of the topic of the `job` to each of its subscribers, by `opts`. The content is the job's :content, a map of the :body bytes and the :content-type, or else what the topic gives when the hub fetches it. Each delivery gives a report of its :outcome: :delivered, :gone for a subscription that the subscriber deleted, :failed with the :retry-at of its retry, or :expired or :stale when there was nothing to deliver. It gives the job with the :deliveries, or with the :error of a fetch, and then the fetch's retry goes to :schedule!.
(distribution subscription content hub-url)(distribution {:keys [topic callback secret] :as subscription}
{:keys [body content-type] :as content}
hub-url
method)The request map that delivers the content of a topic, a map of its
:body and :content-type, to the subscription from the hub at
hub-url, signed by method, sha256 by default, when the subscription
has a secret.
The request map that delivers the `content` of a topic, a map of its :body and :content-type, to the `subscription` from the hub at `hub-url`, signed by `method`, sha256 by default, when the subscription has a secret.
(handler opts)A Ring handler for the URL of the hub, by opts, for the requests of
subscribers and publishers.
It answers 202 to a request that it takes, and gives the job that follows to :schedule!: a check of a subscription or an unsubscription, or the delivery of a topic that a publisher says has changed. It answers 400 with the reason in plain text to a request that's wrong, and 403 to one that the :accept-pred refuses.
A publisher names the topic in the :topic-fields, hub.url as most hubs
take it, or hub.topic. The opts are those of run-job!, and :moved-to,
the URL of another hub that takes the requests from now on.
A Ring handler for the URL of the hub, by `opts`, for the requests of subscribers and publishers. It answers 202 to a request that it takes, and gives the job that follows to :schedule!: a check of a subscription or an unsubscription, or the delivery of a topic that a publisher says has changed. It answers 400 with the reason in plain text to a request that's wrong, and 403 to one that the :accept-pred refuses. A publisher names the topic in the :topic-fields, hub.url as most hubs take it, or hub.topic. The `opts` are those of run-job!, and :moved-to, the URL of another hub that takes the requests from now on.
(lease-seconds asked)(lease-seconds asked
{:keys [lease-seconds min-lease-seconds max-lease-seconds]
:as opts})The lease of a subscription whose request asked for asked seconds, or
for none when nil, by the lease options of opts, those of
default-options by default. It's never endless.
The lease of a subscription whose request asked for `asked` seconds, or for none when nil, by the lease options of `opts`, those of default-options by default. It's never endless.
(memory-store)(memory-store a)A store of subscriptions in the atom a, or in a new one, as a map by
topic and then by callback.
A store is a map of two functions, which can give their results as promises in ClojureScript:
A store of subscriptions in the atom `a`, or in a new one, as a map by topic and then by callback. A store is a map of two functions, which can give their results as promises in ClojureScript: - :subscriptions, a function of a topic that gives its subscriptions - :update!, a function of a topic, a callback and a function f, which replaces their subscription with what f gives for it, nil for none, in one step
(next-attempt attempt at)(next-attempt attempt at retries)When to try again, after the failure of the attempt numbered attempt,
from 0, at the instant at, by the seconds to wait in retries, those
of default-options by default. Nil when the retries are used up.
When to try again, after the failure of the attempt numbered `attempt`, from 0, at the instant `at`, by the seconds to wait in `retries`, those of default-options by default. Nil when the retries are used up.
(run-job! job opts)Do the job by opts, with verify!, distribute!, deliver! or deny! by
its :job, and give the report. In ClojureScript, it gives a promise.
The opts are those of default-options, and these:
Do the `job` by `opts`, with verify!, distribute!, deliver! or deny! by its :job, and give the report. In ClojureScript, it gives a promise. The `opts` are those of default-options, and these: - :url, the URL of the hub - :store, the store of the subscriptions, e.g. memory-store - :schedule!, a function of a job that runs it with run-job! at its :at, or soon when it has none - :denial-fn, a function of a subscription's job that gives a reason to deny it, or nil
(verification job challenge)(verification {:keys [mode topic callback] :as job} challenge lease-seconds)The request map that asks the subscriber of the job whether it wants
it, with the challenge to give back and, for a subscription, its lease
of lease-seconds.
The request map that asks the subscriber of the `job` whether it wants it, with the `challenge` to give back and, for a subscription, its lease of `lease-seconds`.
(verified? challenge {:keys [status body] :as response})Whether the subscriber's response confirms the check with the
challenge: a success whose body is the challenge.
Whether the subscriber's `response` confirms the check with the `challenge`: a success whose body is the challenge.
(verify! {:keys [mode] :as job} opts)Check with the subscriber that it asked for the subscription or the
unsubscription of the job, by opts, and save or delete the
subscription when it did.
A subscription goes to the :denial-fn of opts first, if there is one,
a function of the job that gives a reason to deny it, or nil. A denied
subscription is told so and isn't saved. It gives the job with
:verified? and the subscriber's :status or the :error, or with the
:denied reason.
Check with the subscriber that it asked for the subscription or the unsubscription of the `job`, by `opts`, and save or delete the subscription when it did. A subscription goes to the :denial-fn of `opts` first, if there is one, a function of the job that gives a reason to deny it, or nil. A denied subscription is told so and isn't saved. It gives the job with :verified? and the subscriber's :status or the :error, or with the :denied reason.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |