Nomád Documentation

Federated Search


The federated search feature was added to provide individual control over the
ability to search your stream and personal content.

Previously this was a site security setting made by the site admin.

Implementation Details


Search results are available either via the web or via ActivityPub endpoints. There are two endpoints available. The first is at [https://example.com]/search and searches the entire site. The second is an individual channel search at [https://example.com]/search/[username] . This only searches an individual channel.

Access is controlled by a permission named 'search_stream'. Only viewers possessing both 'view_stream' and 'search_stream' permission can search your channel content. This permission can be assigned individually, or automatically by the channel Permission Role and the personal Roles app.

The site search is an aggregate of all channels that allow the viewer to search their content. The search results may include protected or private content which is permitted to be seen by the viewer. If the viewer cannot be determined, only public content is returned.

The search permission is available to federated protocols via an attribute 'canSearch' in the actor record. This consists of an id or array consisting of the actor ids of those who are permitted to search content. Usually this will be an access list or the ActivityPub 'followers' collection. It may also contain individual actor ids or the ActivityPub "public inbox" identifier if public searching is allowed. An empty entry means nobody can search the channel.


Discovery


The channel search URL is provided in the ActivityPub and Nomad actor records in the optional 'endpoints' section. The names used are 'nomad:searchContent' and 'nomad:searchTags'. These endpoints will be essentially a template, containing a bracket pair {} which is then substituted with the desired search text. This URL provides results for both HTML and ActivityPub/ActivityStreams/Nomad/Zot6 requests.

Example ActivityPub actor record with 'canSearch' attribute and search endpoints
{
    "@context": [
        "https://www.w3.org/ns/activitystreams",
        "https://w3id.org/security/v1",
        {
            "nomad": "https://macgirvin.com/apschema#",
            "toot": "http://joinmastodon.org/ns#",
            "litepub": "http://litepub.social/ns#",
            "sm": "http://smithereen.software/ns#",
            "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
            "oauthRegistrationEndpoint": "litepub:oauthRegistrationEndpoint",
            "sensitive": "as:sensitive",
            "movedTo": "as:movedTo",
            "alsoKnownAs": "as:alsoKnownAs",
            "EmojiReact": "as:EmojiReact",
            "discoverable": "toot:discoverable",
            "wall": "sm:wall",
            "capabilities": "litepub:capabilities",
            "acceptsJoins": "litepub:acceptsJoins",
            "Hashtag": "as:Hashtag",
            "canReply": "toot:canReply",
            "approval": "toot:approval",
            "isContainedConversation": "nomad:isContainedConversation",
            "conversation": "nomad:conversation",
            "commentPolicy": "nomad:commentPolicy",
            "eventRepeat": "nomad:eventRepeat",
            "emojiReaction": "nomad:emojiReaction",
            "expires": "nomad:expires",
            "directMessage": "nomad:directMessage",
            "Category": "nomad:Category",
            "replyTo": "nomad:replyTo",
            "copiedTo": "nomad:copiedTo",
            "canSearch": "nomad:canSearch",
            "searchContent": "nomad:searchContent",
            "searchTags": "nomad:searchTags"
        }
    ],
    "type": "Person",
    "id": "https://macgirvin.com/channel/mike",
    "preferredUsername": "mike",
    "name": "Mike Macgirvin",
    "updated": "2023-07-07T09:24:00Z",
    "icon": {
        "type": "Image",
        "mediaType": "image/jpeg",
        "updated": "2022-11-25T22:10:44Z",
        "url": "https://macgirvin.com/photo/profile/l/5",
        "height": 300,
        "width": 300
    },
    "url": "https://macgirvin.com/channel/mike",
    "location": {
        "type": "Place",
        "name": "Bugger All, Australia"
    },
    "tag": [
        {
            "type": "Note",
            "name": "Protocol",
            "content": "zot6"
        },
        {
            "type": "Note",
            "name": "Protocol",
            "content": "nomad"
        },
        {
            "type": "Note",
            "name": "Protocol",
            "content": "activitypub"
        }
    ],
    "inbox": "https://macgirvin.com/inbox/mike",
    "outbox": "https://macgirvin.com/outbox/mike",
    "followers": "https://macgirvin.com/followers/mike",
    "following": "https://macgirvin.com/following/mike",
    "wall": "https://macgirvin.com/outbox/mike",
    "endpoints": {
        "sharedInbox": "https://macgirvin.com/inbox",
        "oauthRegistrationEndpoint": "https://macgirvin.com/api/client/register",
        "oauthAuthorizationEndpoint": "https://macgirvin.com/authorize",
        "oauthTokenEndpoint": "https://macgirvin.com/token",
        "searchContent": "https://macgirvin.com/search/mike/?search={}",
        "searchTags": "https://macgirvin.com/search/mike/?tag={}"
    },
    "discoverable": true,
    "canSearch": "https://macgirvin.com/followers/mike",
    "publicKey": {
        "id": "https://macgirvin.com/channel/mike?operation=getkey",
        "owner": "https://macgirvin.com/channel/mike",
        "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
        "publicKeyPem": "-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoaTPG5iYKsHJ1cK5CJUy
iN2y6B7aI4JkKMjjZO0gy8+6oa5kx6H5w7qED937a/SvwuYxh1A5yO9nwoEarM5s
PoYZ+Z+GGbAcvzdWURtDDdRMNgktAayDiOvEdiEbgPVx8f39YnpX39ngM8ukob16
S8eNwjWG6uwBs6rxSA409fkWjjbQwbe8fNOpynFWoG8jrB+dK6huryYqkyf9S18u
01IAJOo1ErtaUNkSzkeudXSWokRbN/P77N8LQXorwPF9U6ODblX9QXCWl6EnQ0CX
fcC/3NM6uXfda2KTn83G1+mo5QgGYBjGzE9K1VngoyX4J8AqvQxoXkqV20vwFSqW
ccB13F5kqRQ4BoQm2v2/e65YzjrHwkUecj7tS8TVXu8z4mdbDDbso/UrS14JmrJh
jnbwPOYpHX/6p2SdYDTF/vUWUmeSatS7sHK92eTRukuONig+PNvx8GUtxgMWPIgH
jIupGnR5lZxFMP+iaAmfxOSbVNeLn/Nka7+UfkDThApfhesBA6P8jAdStTCyqAYB
Y3rTTwplcaKKnNv+pLqBqyhYEghmGvv2EC2UGsL6rFit1RaZgSXWCIcLzdRZo4Ak
znvz8+juMjyPLp7DdSHhKss9kV9HDxZXjrstDxOR61j0vifaMh6bUVrOAMm0Ffs+
41v+D6pSA5p0OI2aqNJzLZ8CAwEAAQ==
-----END PUBLIC KEY-----
"
    },
    "manuallyApprovesFollowers": true,
    "image": {
        "type": "Image",
        "mediaType": "image/jpeg",
        "url": "https://macgirvin.com/photo/aea3d61f-5f31-4572-8249-dd01e70ab7d8-7"
    },
    "summary": "Farmer in Bugger All, Australia. Plays a mean guitar, upside down and backwards. Systems integration, software development, and robot repair.",
    "attachment": [
        {
            "type": "Note",
            "name": "birthday",
            "content": "0000-00-00"
        }
    ],
    "signature": {
        "type": "RsaSignature2017",
        "nonce": "8f848b54a05750904e60fa7a770f130c6ec9c19b9c5a29bdd2ebd83e4e679575",
        "creator": "https://macgirvin.com/channel/mike",
        "created": "2023-08-29T20:37:56Z",
        "signatureValue": "PcNuQ7sZ5qHk1UIi0UsBlHdm4AolT6iEnVWfGmi292Is7A2vFVEMutmWb7y/OUy9b0kLq9o2ScAMbv3vkmKQcyWmk/qez0koMWoHmsxMcYWJE7rxnZWrulMSomRO0qNGgX+D2TsLRsGd5kktC/PgRbqD4OKzvV9jTgGnGaEqb1CYTQp9X/w66zG/jV+y94NTIe60iG8xVsLtLZRw/Y+LCu06BKfvDajQit9WSNVZVnqCub8IR63SxFtyWNZuolGpzjVW7z3C86Tj3Sv89lonf5XtR6JEUniFHZokbL4Ie23fbK4IKVAu5AriBYlMK2hMcpxKxFfHgOjav7B9yxze4QV10LjxGHZUklgzJwQUBX92RmZIpu5Cdsw94kNLnq1Xy5b6Xyam8TiPBOgEnUiZ74zDi3e9WKqxvTp2dsgC3g+sCCSQ5u6388nLzhREQWEwAlvhQ5p3M1gpg6P0S9HFA9syVjP0g2tlE3jK1RfUY/1sIp7awb0GdstN5iVF8BHDmd+uYaTZxfKw/cZ+Wa6IOVlAD0wSZnlvsms/DvPeMOcn7Pd+TxiJm4flSqFdkEDABc5WTFikHDw6O3KZTbhyeXKLyWNxTch/kwCo+6Fi6S3Wj38IkP7spuuw+VFxetIo3n44oGXzu4YuBII3ZbUG/Kp9V+OVHP5t+hs4qTWhvMQ="
    }
}

A similar record is provided for the site and is located at the site or domain root. It is identical, except that the site search presents an aggregated search, and can also be separately controlled by a site security option "Block Public Search".

If a channel has federated search enabled, an entry is provided in the author action drop down attached to every post/comment. This performs an HTML search of the provided endpoint(s).

Opensearch


Search endpoint discovery is also provided by Opensearch. The opensearch definition is provided in webfinger for either a channel or for the site (identified by the actor record for the site/domain root).

Example webfinger entry at domain root:
    {
        "rel": "http://a9.com/-/spec/opensearch/1.1/",
        "type": "application/opensearchdescription+xml",
        "href": "https://macgirvin.com/opensearch",
        "title": "macgirvin.com"
    }

Example webfinger entry for an individual actor:
    {
        "rel": "http://a9.com/-/spec/opensearch/1.1/",
        "type": "application/opensearchdescription+xml",
        "href": "https://macgirvin.com/opensearch/mike",
        "title": "mike@macgirvin.com"
    }

This links to the following opensearch document:
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
    <ShortName>mike@macgirvin.com</ShortName>
    <Description>Search Zap|mike@macgirvin.com</Description>
    <Image height="64" width="64" type="image/png">https://macgirvin.com/photo/profile/s/5</Image>
    <Url type="text/html" template="https://macgirvin.com/search/mike?search={searchTerms}"/>
    <Url type="application/ld+json; profile="https://www.w3.org/ns/activitystreams"" template="https://macgirvin.com/search/mike?search={searchTerms}"/>
</OpenSearchDescription>

The second Url in the example is a pointer to the ActivityPub enabled endpoint. A similar document is provided for the site search. Since an HTML Url is provided, these searches will be available as a browser search engine from many browsers and are also available to any opensearch enabled service. These services will only search public content (if public searches are permitted) as there is currently no mechanism for opensearch to determine the identity of the viewer. There are plans to integrate this with OpenWebAuth, so this restriction is temporary. [Edit: this was done.]

The opensearch links are also provided as links in the actor's channel page. Both the channel search and the site search are listed.
<link rel="search" href="https://macgirvin.com/opensearch" type="application/opensearchdescription+xml" title="Search zap (macgirvin.com)" />
<link rel="search" href="https://macgirvin.com/opensearch/mike" type="application/opensearchdescription+xml" title="mike@macgirvin.com" />