Skip to content

fedify: CLI toolchain

The fedify command is a CLI toolchain for debugging ActivityPub-enabled federated server apps. Although it is primarily designed for developers who use Fedify, it can be used with any ActivityPub-enabled server.

Installation

Using Deno

If you have Deno installed, you can install fedify by running the following command:

sh
deno install \
  -A \
  --unstable-fs --unstable-kv --unstable-temporal \
  -n fedify \
  jsr:@fedify/cli
powershell
deno install `
  -A `
  --unstable-fs --unstable-kv --unstable-temporal `
  -n fedify `
  jsr:@fedify/cli

Downloading the executable

You can download the pre-built executables from the releases page. Download the appropriate executable for your platform and put it in your PATH.

fedify lookup: Looking up an ActivityPub object

The fedify lookup command is used to look up an ActivityPub object by its URL or an actor by its handle.

For example, the below command looks up a Note object with the given URL:

sh
fedify lookup https://todon.eu/@hongminhee/112341925069749583

The output will be like the below:

Note {
  id: URL "https://todon.eu/users/hongminhee/statuses/112341925069749583",
  attachments: [
    Document {
      name: "The demo video on my terminal",
      url: URL "https://todon.eu/system/media_attachments/files/112/341/916/300/016/369/original/f83659866f94054f.mp"... 1 more character,
      mediaType: "video/mp4"
    }
  ],
  attribution: URL "https://todon.eu/users/hongminhee",
  contents: [
    '<p>I&#39;m working on adding a CLI toolchain to <a href="https://todon.eu/tags/Fedify" class="mentio'... 379 more characters,
    <en> '<p>I&#39;m working on adding a CLI toolchain to <a href="https://todon.eu/tags/Fedify" class="mentio'... 379 more characters
  ],
  published: 2024-04-27T07:08:57Z,
  replies: Collection {
    id: URL "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies",
    first: CollectionPage {
      items: [
        URL "https://todon.eu/users/hongminhee/statuses/112343493232608516"
      ],
      partOf: URL "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies",
      next: URL "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies?min_id=112343493232608516&page"... 5 more characters
    }
  },
  url: URL "https://todon.eu/@hongminhee/112341925069749583",
  to: URL "https://www.w3.org/ns/activitystreams#Public",
  cc: URL "https://todon.eu/users/hongminhee/followers",
  sensitive: false
}

-c/--compact: Compact JSON-LD

You can also output the object in the compacted JSON-LD format by using the -c/--compact option:

sh
fedify lookup --compact https://todon.eu/@hongminhee/112341925069749583

The output will be like the below:

json
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://todon.eu/users/hongminhee/statuses/112341925069749583",
  "type": "Note",
  "attachment": {
    "type": "Document",
    "mediaType": "video/mp4",
    "name": "The demo video on my terminal",
    "url": "https://todon.eu/system/media_attachments/files/112/341/916/300/016/369/original/f83659866f94054f.mp4"
  },
  "attributedTo": "https://todon.eu/users/hongminhee",
  "cc": "https://todon.eu/users/hongminhee/followers",
  "content": "<p>I&#39;m working on adding a CLI toolchain to <a href=\"https://todon.eu/tags/Fedify\" class=\"mention hashtag\" rel=\"tag\">#<span>Fedify</span></a> to help with debugging.  The first feature I implemented is the ActivityPub object lookup.</p><p>Here&#39;s a demo.</p><p><a href=\"https://todon.eu/tags/fedidev\" class=\"mention hashtag\" rel=\"tag\">#<span>fedidev</span></a> <a href=\"https://todon.eu/tags/ActivityPub\" class=\"mention hashtag\" rel=\"tag\">#<span>ActivityPub</span></a></p>",
  "contentMap": {
    "en": "<p>I&#39;m working on adding a CLI toolchain to <a href=\"https://todon.eu/tags/Fedify\" class=\"mention hashtag\" rel=\"tag\">#<span>Fedify</span></a> to help with debugging.  The first feature I implemented is the ActivityPub object lookup.</p><p>Here&#39;s a demo.</p><p><a href=\"https://todon.eu/tags/fedidev\" class=\"mention hashtag\" rel=\"tag\">#<span>fedidev</span></a> <a href=\"https://todon.eu/tags/ActivityPub\" class=\"mention hashtag\" rel=\"tag\">#<span>ActivityPub</span></a></p>"
  },
  "published": "2024-04-27T07:08:57Z",
  "replies": {
    "id": "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies",
    "type": "Collection",
    "first": {
      "type": "CollectionPage",
      "items": "https://todon.eu/users/hongminhee/statuses/112343493232608516",
      "next": "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies?min_id=112343493232608516&page=true",
      "partOf": "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies"
    }
  },
  "as:sensitive": false,
  "to": "as:Public",
  "url": "https://todon.eu/@hongminhee/112341925069749583"
}

-e/--expanded: Expanded JSON-LD

You can also output the object in the expanded JSON-LD format by using the -e/--expanded option:

sh
fedify lookup --expand https://todon.eu/@hongminhee/112341925069749583

The output will be like the below:

json
[
  {
    "@id": "https://todon.eu/users/hongminhee/statuses/112341925069749583",
    "@type": [
      "https://www.w3.org/ns/activitystreams#Note"
    ],
    "https://www.w3.org/ns/activitystreams#attachment": [
      {
        "@type": [
          "https://www.w3.org/ns/activitystreams#Document"
        ],
        "https://www.w3.org/ns/activitystreams#mediaType": [
          {
            "@value": "video/mp4"
          }
        ],
        "https://www.w3.org/ns/activitystreams#name": [
          {
            "@value": "The demo video on my terminal"
          }
        ],
        "https://www.w3.org/ns/activitystreams#url": [
          {
            "@id": "https://todon.eu/system/media_attachments/files/112/341/916/300/016/369/original/f83659866f94054f.mp4"
          }
        ]
      }
    ],
    "https://www.w3.org/ns/activitystreams#attributedTo": [
      {
        "@id": "https://todon.eu/users/hongminhee"
      }
    ],
    "https://www.w3.org/ns/activitystreams#cc": [
      {
        "@id": "https://todon.eu/users/hongminhee/followers"
      }
    ],
    "https://www.w3.org/ns/activitystreams#content": [
      {
        "@value": "<p>I&#39;m working on adding a CLI toolchain to <a href=\"https://todon.eu/tags/Fedify\" class=\"mention hashtag\" rel=\"tag\">#<span>Fedify</span></a> to help with debugging.  The first feature I implemented is the ActivityPub object lookup.</p><p>Here&#39;s a demo.</p><p><a href=\"https://todon.eu/tags/fedidev\" class=\"mention hashtag\" rel=\"tag\">#<span>fedidev</span></a> <a href=\"https://todon.eu/tags/ActivityPub\" class=\"mention hashtag\" rel=\"tag\">#<span>ActivityPub</span></a></p>"
      },
      {
        "@language": "en",
        "@value": "<p>I&#39;m working on adding a CLI toolchain to <a href=\"https://todon.eu/tags/Fedify\" class=\"mention hashtag\" rel=\"tag\">#<span>Fedify</span></a> to help with debugging.  The first feature I implemented is the ActivityPub object lookup.</p><p>Here&#39;s a demo.</p><p><a href=\"https://todon.eu/tags/fedidev\" class=\"mention hashtag\" rel=\"tag\">#<span>fedidev</span></a> <a href=\"https://todon.eu/tags/ActivityPub\" class=\"mention hashtag\" rel=\"tag\">#<span>ActivityPub</span></a></p>"
      }
    ],
    "https://www.w3.org/ns/activitystreams#published": [
      {
        "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
        "@value": "2024-04-27T07:08:57Z"
      }
    ],
    "https://www.w3.org/ns/activitystreams#replies": [
      {
        "@id": "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies",
        "@type": [
          "https://www.w3.org/ns/activitystreams#Collection"
        ],
        "https://www.w3.org/ns/activitystreams#first": [
          {
            "@type": [
              "https://www.w3.org/ns/activitystreams#CollectionPage"
            ],
            "https://www.w3.org/ns/activitystreams#items": [
              {
                "@id": "https://todon.eu/users/hongminhee/statuses/112343493232608516"
              }
            ],
            "https://www.w3.org/ns/activitystreams#next": [
              {
                "@id": "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies?min_id=112343493232608516&page=true"
              }
            ],
            "https://www.w3.org/ns/activitystreams#partOf": [
              {
                "@id": "https://todon.eu/users/hongminhee/statuses/112341925069749583/replies"
              }
            ]
          }
        ]
      }
    ],
    "https://www.w3.org/ns/activitystreams#sensitive": [
      {
        "@value": false
      }
    ],
    "https://www.w3.org/ns/activitystreams#to": [
      {
        "@id": "https://www.w3.org/ns/activitystreams#Public"
      }
    ],
    "https://www.w3.org/ns/activitystreams#url": [
      {
        "@id": "https://todon.eu/@hongminhee/112341925069749583"
      }
    ]
  }
]

Looking up an actor by handle

You can also look up an actor by its handle or URL. For example, the below command looks up an actor with the given handle:

sh
fedify lookup @fedify-example@fedify-blog.deno.dev

The output will be like the below:

Person {
  id: URL "https://fedify-blog.deno.dev/users/fedify-example",
  name: "Fedify Example Blog",
  published: 2024-03-03T13:18:11.857384756Z,
  summary: "This blog is powered by Fedify, a fediverse server framework.",
  url: URL "https://fedify-blog.deno.dev/",
  preferredUsername: "fedify-example",
  publicKey: CryptographicKey {
    id: URL "https://fedify-blog.deno.dev/users/fedify-example#main-key",
    owner: URL "https://fedify-blog.deno.dev/users/fedify-example",
    publicKey: CryptoKey {
      type: "public",
      extractable: true,
      algorithm: {
        name: "RSASSA-PKCS1-v1_5",
        modulusLength: 4096,
        publicExponent: Uint8Array(3) [ 1, 0, 1 ],
        hash: { name: "SHA-256" }
      },
      usages: [ "verify" ]
    }
  },
  inbox: URL "https://fedify-blog.deno.dev/users/fedify-example/inbox",
  outbox: URL "https://fedify-blog.deno.dev/users/fedify-example/outbox",
  following: URL "https://fedify-blog.deno.dev/users/fedify-example/following",
  followers: URL "https://fedify-blog.deno.dev/users/fedify-example/followers",
  endpoints: Endpoints { sharedInbox: URL "https://fedify-blog.deno.dev/inbox" },
  discoverable: true,
  suspended: false,
  memorial: false,
  indexable: true
}

You can omit the @ prefix when looking up an actor by handle:

sh
fedify lookup fedify-example@fedify-blog.deno.dev

Or you can look up an actor by acct: URL:

sh
fedify lookup acct:fedify-example@fedify-blog.deno.dev

-a/--authorized-fetch: Authorized fetch

You can also use the -a/--authorized-fetch option to fetch the object with authentication. Under the hood, this option generates an one-time key pair, spins up a temporary ActivityPub server to serve the public key, and signs the request with the private key.

Here's an example where the fedify lookup fails due to the object being protected:

sh
fedify lookup @tchambers@indieweb.social

The above command will output the below error:

Failed to fetch the object.
It may be a private object.  Try with -a/--authorized-fetch.

However, you can fetch the object with the -a/--authorized-fetch option:

sh
fedify lookup --authorized-fetch @tchambers@indieweb.social

This time, the above command will output the object successfully:

Person {
  id: URL "https://indieweb.social/users/tchambers",
  attachments: [
    PropertyValue {
      name: "Indieweb Site",
      value: '<a href="http://www.timothychambers.net" target="_blank" rel="nofollow noopener noreferrer me" trans'... 128 more characters
    },
    PropertyValue {
      name: "Gravatar",
      value: '<a href="https://en.gravatar.com/tchambers" target="_blank" rel="nofollow noopener noreferrer me" tr'... 134 more characters
    },
    PropertyValue {
      name: "Threads",
      value: '<a href="https://www.threads.net/@timothyjchambers" target="_blank" rel="nofollow noopener noreferre'... 150 more characters
    },
    PropertyValue {
      name: "GitHub",
      value: '<a href="https://github.com/Timothyjchambers" target="_blank" rel="nofollow noopener noreferrer me" '... 138 more characters
    }
  ],
  name: "Tim Chambers",
  icon: Image {
    url: URL "https://cdn.masto.host/indiewebsocial/accounts/avatars/000/000/002/original/5de753df6fe336d5.png",
    mediaType: "image/png"
  },
  image: Image {
    url: URL "https://cdn.masto.host/indiewebsocial/accounts/headers/000/000/002/original/38c44f4142b84cf4.png",
    mediaType: "image/png"
  },
  published: 2019-08-30T00:00:00Z,
  summary: "<p>Technologist, writer, admin of indieweb.social. Fascinated by how new politics impacts technology"... 346 more characters,
  url: URL "https://indieweb.social/@tchambers",
  preferredUsername: "tchambers",
  publicKey: CryptographicKey {
    id: URL "https://indieweb.social/users/tchambers#main-key",
    owner: URL "https://indieweb.social/users/tchambers",
    publicKey: CryptoKey {
      type: "public",
      extractable: true,
      algorithm: {
        name: "RSASSA-PKCS1-v1_5",
        modulusLength: 2048,
        publicExponent: Uint8Array(3) [ 1, 0, 1 ],
        hash: { name: "SHA-256" }
      },
      usages: [ "verify" ]
    }
  },
  manuallyApprovesFollowers: false,
  inbox: URL "https://indieweb.social/users/tchambers/inbox",
  outbox: URL "https://indieweb.social/users/tchambers/outbox",
  following: URL "https://indieweb.social/users/tchambers/following",
  followers: URL "https://indieweb.social/users/tchambers/followers",
  endpoints: Endpoints { sharedInbox: URL "https://indieweb.social/inbox" },
  discoverable: true,
  memorial: false,
  indexable: true
}

fedify inbox: Ephemeral inbox server

The fedify inbox command is used to spin up an ephemeral server that serves the ActivityPub inbox with an one-time actor, through a short-lived public DNS with HTTPS. This is useful when you want to test and debug the outgoing activities of your server. To start an ephemeral inbox server, run the below command:

sh
fedify inbox

If it goes well, you will see the output like the below (without termination; press ^C to stop the server):

✔ The ephemeral ActivityPub server is up and running: https://12a4fea81cbcf6.lhr.life/
✔ Followed @bibelus_vasariol@activitypub.academy
╭───────────────┬─────────────────────────────────────────╮
│ Actor handle: │ i@12a4fea81cbcf6.lhr.life               │
├───────────────┼─────────────────────────────────────────┤
│    Actor URI: │ https://12a4fea81cbcf6.lhr.life/i       │
├───────────────┼─────────────────────────────────────────┤
│  Actor inbox: │ https://12a4fea81cbcf6.lhr.life/i/inbox │
├───────────────┼─────────────────────────────────────────┤
│ Shared inbox: │ https://12a4fea81cbcf6.lhr.life/inbox   │
╰───────────────┴─────────────────────────────────────────╯

Although the given URIs and handle are short-lived, they are anyway publicly dereferenceable until the server is terminated. You can use these URIs and handle to test and debug the outgoing activities of your server.

If any incoming activities are received, the server will log them to the console:

╭────────────────┬────────────────────────────────────╮
│     Request #: │ 3                                  │
├────────────────┼────────────────────────────────────┤
│ Activity type: │ Create                             │
├────────────────┼────────────────────────────────────┤
│  HTTP request: │ POST /inbox                        │
├────────────────┼────────────────────────────────────┤
│ HTTP response: │ 202                                │
├────────────────┼────────────────────────────────────┤
│        Details │ http://12a4fea81cbcf6.lhr.life/r/3 │
╰────────────────┴────────────────────────────────────╯

You can also see the details of the incoming activities by visiting the /r/:id endpoint of the server in your browser:

The details of the incoming activities

-f/--follow: Follow an actor

The -f/--follow option is used to follow an actor. You can specify the actor handle or URI to follow. For example, to follow the actor with the handle @john@doe.com and @jane@doe.com, run the below command:

sh
fedify inbox -f @john@doe.com -f @jane@doe.com

NOTE

Although -f/--follow option sends Follow activities to the specified actors, it does not guarantee that they will accept the follow requests. If the actors accept the follow requests, you will receive the Accept activities in the inbox server, and the server will log them to the console:

╭────────────────┬─────────────────────────────────────╮
│     Request #: │ 0                                   │
├────────────────┼─────────────────────────────────────┤
│ Activity type: │ Accept                              │
├────────────────┼─────────────────────────────────────┤
│  HTTP request: │ POST /i/inbox                       │
├────────────────┼─────────────────────────────────────┤
│ HTTP response: │ 202                                 │
├────────────────┼─────────────────────────────────────┤
│        Details │ https://876f71397f5c31.lhr.life/r/0 │
╰────────────────┴─────────────────────────────────────╯

-a/--accept-follow: Accept follow requests

The -a/--accept-follow option is used to accept follow requests from actors. You can specify the actor handle or URI to accept follow requests. Or you can accept all follow requests by specifying the wildcard *. For example, to accept follow requests from the actor with the handle @john@doe.com and @jane@doe.com, run the below command:

sh
fedify inbox -a @john@doe.com -a @jane@doe.com

When the follow requests are received from the specified actors, the server will immediately send the Accept activities to them. Otherwise, the server will just log the Follow activities to the console without sending the Accept activities.

-T/--no-tunnel: Local server without tunneling

The -T/--no-tunnel option is used to disable the tunneling feature of the inbox server. By default, the inbox server tunnels the local server to the public internet, so that the server is accessible from the outside. If you want to disable the tunneling feature, run the below command:

sh
fedify inbox --no-tunnel

It would be useful when you want to test the server locally but are worried about the security implications of exposing the server to the public internet.

NOTE

If you disable the tunneling feature, the ephemeral ActivityPub instance will be served via HTTP instead of HTTPS.

Shell completions

The fedify command supports shell completions for Bash, Fish, and Zsh.

Bash

To enable Bash completions add the following line to your profile file (~/.bashrc, ~/.bash_profile, or ~/.profile):

bash
source <(fedify completions bash)

Fish

To enable Fish completions add the following line to your profile file (~/.config/fish/config.fish):

fish
source (fedify completions fish | psub)

Zsh

To enable Zsh completions add the following line to your profile file (~/.zshrc):

zsh
source <(fedify completions zsh)