--- title: "WebSub Hub" slug: "websub-hub" updated: 2023-02-14T17:35:59Z published: 2023-02-14T17:35:59Z canonical: "docs.echo.stream/websub-hub" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.echo.stream/llms.txt > Use this file to discover all available pages before exploring further. # WebSub Hub WebSub Hub Nodes are Nodes that implement the Hub actor in the [WebSub specification](https://www.w3.org/TR/websub/). Hubs sit between the Publisher and Subscribers. They support registration and maintenance of subscriptions to topics and the distribution of topic content updates from the Publisher to the topic's Subscribers. All content distribution is accomplished via webhooks. Any MIME type may be distributed by the Publisher. WebSub Hubs Nodes receive `echo.websub` Messages from the Publisher (which is probably another Node or Nodes in your Tenant) and route those to active subscribers based upon the Topic URL. Just like all other Nodes in EchoStream, Messages that you send to the WebSub Hub Node are guaranteed to be delivered (unless you configure `deliveryRetries` > 0) and are guaranteed to be in order. Authentication and authorization of new Subscribers is accomplished via the Api Authenticator Function that you provide to the WebSub Hub Node. ## Content Distribution WebSub Hub Nodes accept `echo.websub` Messages for WebSub content distribution. The `echo.websub` Message type is a JSON message and has the following attributes: - **contentBase64**: The actual content that will be distributed. Must be binary in base64 encoding. Conflicts with `contentUrl`, and will not be used if that field is present. If this is present and `contentType` is not present, `text/plain` is assumed as the `contentType` to send. The contents of this field will be sent as-is. - **contentType**: The MIME type of the content. If is not present and `contentUrl` is present, will use the `Content-Type` received from the `contentUrl` if present. If it is present it will *always* be the `Content-Type` sent to the subscriber. - **contentUrl**: The URL to retrive the content to send using HTTP GET. Conflicts with `contentBase64`; if both are present `contentUrl` will be used *unless* the provided URL returns an error code. The body retrieved from the URL will be sent to the subscriber as-is. - **topic**: The topic URL that this content is for. **REQUIRED** WebSub Hub Nodes are capable of distributing **any** Content-Type. The Content-Type being distributed *should* match the content type negotiated during topic discovery. > Note - While WebSub Hubs (and Webhooks in general) are normally used to distribute text-based content (e.g. - JSON, XML, Plain text), the WebSub standard itself makes no such restrictions; in fact, the standard explicitly states that the Hub *must* be content unaware. ## Api Authenticator Function The Api Authenticator Function that you give the WebSub Hub Node controls how the Hub responds to new subscription requests. You may provide this Python function either directly in the Webhook Node (an inline function) or by specifying the name of a Api Authenticator Function in your Tenant's Function Library. If you do not provide an Api Authenticator Function, all subscription requests will be *denied*. Api Authenticator functions are also allowed to have the `async` keyword prior to their definition. Api Authenticator functions used in WebSub Hub Nodes must conform to the following template: ``` def authenticator(*, context, request, **kwargs): from typing import TYPE_CHECKING, cast from fastapi import HTTPException from starlette.authentication import AuthCredentials, BaseUser, SimpleUser if TYPE_CHECKING: from echostream_function_context import Context from fastapi import Request context = cast(Context, context) request = cast(Request, request) # TODO - Perform authentication using the context and request here # You must either return None or return a tuple containing an AuthCredentials # and a subclass of BaseUser. If you return None or a Base User that returns False # from is_authenticated, then the Request will be rejected with a 403. # # The BaseUser subclass that you return MUST implement the identity property, as below. # # The AuthCredentials returned should contain one or more regular expressions that describe # the topics that the subscriber is allowed to subscribe to. In the example below, all topics are # authorized. class WebSubUser(SimpleUser): @property def identity(self) -> str: return self.username return AuthCredentials([r"http[s]?://.*"])), WebSubUser("foobar") ``` > NOTE - you **must** not have any Python code outside of the single function `def` statement!! ### Arguments The arguments for your api authenticator functions are all keyword arguments. An explanation of the arguments are below: | Argument | Type | Description | | --- | --- | --- | | context | object | The [Context](/v1/docs/context-object) object providing execution environment information and helper objects and methods. | | request | fastapi.Request | The [Request](https://fastapi.tiangolo.com/advanced/using-request-directly/?h=request) object providing all attributes of the request as received. | | kwargs | dict | This is present specifically to future-proof your function. If, in the future, EchoStream Webhook Nodes pass additional arguments to your api authenticator function, those additional arguments will not break the call to your function. | ### Return Your processor function must return one of the following types; `None`, `tuple[starlette.authentication.AuthCredentials, starlette.authentication.BaseUser]`. - `None`: The WebSub Hub Node will accept subscription requests from anyone but will deny all of them. - `tuple[starlette.authentication.AuthCredentials, starlette.authentication.BaseUser]`: - `AuthCredentials`: The WebSub Hub Node uses the `scopes` of the `AuthCredentials` to authorize subscriptions to topics. It treats the `scopes` as regular expressions; it the topic URL matches any of the regular expressions in `scopes`, that subscription is accepted; otherwise, the subscription will be denied. - `BaseUser`: The WebSub Hub Node will only accept the request if the `BaseUser` instance returns `True` from `is_authenticated`. Otherwise it will return a `403` to the client. The instance of `BaseUser` that you return **must** implement the `identity` property, as this will be used to identify the subscriber in a subscription. > NOTE - if you return any type other than those specified, the Webhook Node will throw an exception and cease processing. ### Exceptions You may raise a `fastapi.HTTPException` from your api authenticator. The Webhook Node will reject the request and return a response to the client as dictated by the exception. > NOTE - raising `HTTPException` is a effective way to get granular in your response to the client. Any other exception raised will result in a `500` return to the client. ### Requirements You can use any package available on [PyPI](https://pypi.org/) in your Api Authenticator function. Simply add the requirement to the `requirements` of your WebSub Hub Node and it will be included in your Node. Thisis done using [pip](https://pip.pypa.io/en/stable/) [requirement specifiers](https://pip.pypa.io/en/stable/cli/pip_install/#requirement-specifiers). For example, to include the most popular PostgreSQL database adapter for Python (psycopyg), you would put the following requirement in your Node's `requirements`: - For the latest release: ``` psycopg2 ``` - Pinning the release: ``` psycopg2 == 2.9.3 ``` - Ensuring a baseline release ``` psycopg2 >= 2.9.2 ``` Then in your Api Authenticator function, you can use `psycopg2` by importing it, as follows: ``` def authenticator(*, context, request, **kwargs): # Make SURE that you import INSIDE of the function!! import psycopg2 ... ``` ## Subscription Settings WebSub Hub Nodes support several settings that are beyond the scope of the WebSub standard. These settings allow for further control over the security, duration and retries for subscriptions to the Hub. | Name | Description | Default | | --- | --- | --- | | defaultLeaseSeconds | The lease duration to apply to subscription requests that do not specify `hub.lease_seconds` | 864000 | | deliveryRetries | The number of times to attempt delivery to a subscription | Unlimited | | maxLeaseSeconds | The maximum lease duration for a subscription | 864000 | | signatureAlgorithm | The WebSub signature algorithm used by hub subscriptions when the subscription provides a `hub.secret` | sha1 | | subscriptionSecurity | The security requirements the hub is enforcing on subscription requests; you may enforce HTTPS callbacks, `hub.secret`, or both | None |