Skip to content

Commit

Permalink
discovery & webfinger guides
Browse files Browse the repository at this point in the history
  • Loading branch information
michelson committed Sep 12, 2023
1 parent a20651b commit 75cf0d4
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
75 changes: 75 additions & 0 deletions guides/discovery-rails.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Registering with another ActivityPub server, often called "following" in the context of social interactions, typically involves a few steps:

1. **Discovering the Actor on the remote server**: Before you can follow someone (or some entity), you need to discover their unique ActivityPub ID (usually a URL).

2. **Sending a `Follow` activity**: To start the following process, your server sends a `Follow` activity to the remote server.

3. **Remote server responds**: The remote server then acknowledges this with an `Accept` or `Reject` activity.

To implement this in a Rails context, you'd likely want some combination of models, controllers, and views (forms). Here's a basic guide:

### 1. Discovering the Actor:

#### a. Form to input Actor ID:

You can create a simple form where a user can paste or type in the ActivityPub ID of the actor they want to follow.

```erb
<%= form_with(url: discover_actor_path, method: :post) do |f| %>
<%= f.label :actor_id, "Enter Actor's ActivityPub ID" %>
<%= f.text_field :actor_id %>
<%= f.submit "Discover" %>
<% end %>
```

### 2. Sending a `Follow` activity:

#### a. Displaying the discovered Actor:

Once you've fetched the Actor's data (like their name, avatar, summary, etc.), present it to the user and offer an option to follow.

```erb
<% if @actor %>
<h2><%= @actor.name %></h2>
<%= image_tag @actor.avatar_url %>
<p><%= @actor.summary %></p>
<%= button_to "Follow", follow_actor_path(actor_id: @actor.id), method: :post %>
<% end %>
```

#### b. Sending the `Follow` Activity:

When the "Follow" button is pressed, construct and send a `Follow` activity to the remote server.

```ruby
def follow
actor_id = params[:actor_id]
# Construct your `Follow` activity
activity = {
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Follow',
'actor': current_user.activitypub_id,
'object': actor_id
}
# Send this to the remote server
# ...
end
```

### 3. Handling the remote server's response:

You'll want to handle incoming activities (like `Accept` or `Reject` in response to your `Follow`). If the remote actor accepts, store this relationship in your database, perhaps with a `Following` model.

### Tips:

1. **Webfinger**: Some platforms use Webfinger to make discovering users easier. Instead of needing the full ActivityPub ID, you can type in a username like `@user@domain.com`, and the server translates that into an ActivityPub ID.

2. **Security**: Always validate incoming and outgoing data. ActivityPub deals with external servers, so there's an inherent risk.

3. **Feedback for Users**: Give feedback to your users. Let them know when the follow request has been sent, and again when it's been accepted or rejected.

4. **Idempotency**: If a user tries to follow an actor they're already following, handle this gracefully. Maybe send an `Undo` `Follow` activity or simply notify the user they're already following the actor.

5. **Error Handling**: The remote server might not be available, or there might be other issues with the `Follow` request. Handle these gracefully with appropriate error messages.

Following this guide, you can create a simple yet functional interface for your Rails app's users to follow actors on remote ActivityPub servers.
69 changes: 69 additions & 0 deletions guides/webfinger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
Let's outline a basic Webfinger implementation for our Rails context:

### 1. Webfinger Route

First, you need to add a route that will respond to Webfinger queries:

```ruby
# config/routes.rb
get '/.well-known/webfinger', to: 'webfinger#show'
```

### 2. Webfinger Controller

Then, create a controller that handles the Webfinger requests:

```ruby
# app/controllers/webfinger_controller.rb
class WebfingerController < ApplicationController
def show
account = params[:resource].sub('acct:', '')

user = User.find_by(webfinger_account: account)

if user
render json: {
subject: "acct:#{user.webfinger_account}",
links: [
{
rel: 'self',
type: 'application/activity+json',
href: user_activitypub_url(user)
}
]
}
else
render status: :not_found
end
end
end
```

### 3. User Model

You might need a method or attribute for the user's ActivityPub URL. Here's a simple example, assuming `User` has an `username` field:

```ruby
# app/models/user.rb
class User < ApplicationRecord
def webfinger_account
"#{username}@#{Rails.application.routes.default_url_options[:host]}"
end

def activitypub_url(user)
"https://your-domain.com/activitypub/users/#{user.id}"
end
end
```

### 4. Configuration & Middleware

Make sure you have the necessary CORS headers set up, as remote servers will need to access the Webfinger endpoint.

Additionally, consider caching the Webfinger responses, since the data doesn't change frequently. This will reduce the load on your server.

### 5. Documentation

Lastly, document your Webfinger endpoint, so developers from other services know how to discover users on your platform. You might want to link to the official Webfinger specification for further details.

With these steps, you should have a basic Webfinger implementation that allows other servers to discover users on your platform via their `@user@domain.com` identifier.

0 comments on commit 75cf0d4

Please sign in to comment.